使用需要具体大小的特质对象

6
我希望有一个trait对象包装的LinkedList。内部是Ssl或Non-Ssl流的流类型。我的希望是传递结构体包装器,并且只要内部符合相同的trait,无论使用哪种内部流类型,一切都将正常。
简单示例:
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::os::unix::io::{RawFd, AsRawFd};

pub trait HRecv {}
pub trait HSend {}
pub trait HStream: HRecv + HSend + AsRawFd + Clone {}
pub struct Stream<T: HStream> {
    pub inner: T
}

pub type StreamList = Arc<Mutex<LinkedList<Stream<HStream>>>>;

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
}

产生以下错误:
error: the trait 'core::marker::Sized' is not implemented for the type 'HStream' [E0277]
let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                ^~~~~~~~~~~~~~~

我尝试将+ Sized添加到HStream的定义中,以及使inner成为Box<T>,但两者都会产生相同的错误。
在Rust中是否可以实现这个功能?如果可以,语法是什么?
2个回答

6

好的,这里有几个问题。逐个解决编译错误:

<anon>:15:53: 15:68 error: the trait `core::marker::Sized` is not implemented for the type `HStream` [E0277]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0277
<anon>:15:53: 15:68 note: `HStream` does not have a constant size known at compile-time
<anon>:15:53: 15:68 note: required by `Stream`

由于HStream没有在编译时计算出大小,因此无法替换类型参数T所有类型参数都需要隐式要求替换的类型具有编译时大小。如果您想允许动态大小的类型,则需要通过以下方式显式地退出此隐含的约束:

<T: ?Sized + HStream>

<anon>:15:53: 15:68 error: the trait `HStream` is not implemented for the type `HStream` [E0277]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0277
<anon>:15:53: 15:68 note: required by `Stream`

特质本身并不实现。您正在请求一个实现HStream的类型,但是HStream本身没有实现(它怎么实现呢?)。 您必须提供一个实现了HStream的类型。
<anon>:15:53: 15:68 error: the trait `HStream` cannot be made into an object [E0038]
<anon>:15     let mut list = Arc::new(Mutex::new(LinkedList::<Stream<HStream>>::new()));
                                                              ^~~~~~~~~~~~~~~
<anon>:15:53: 15:68 help: see the detailed explanation for E0038
<anon>:15:53: 15:68 note: the trait cannot require that `Self : Sized`

这里有一个K-O问题: HStream不能与动态调度一起使用,没有例外。它不是对象安全的。这很可能是由于需要Clone引起的。
解决上述所有问题的“方法”是重新设计您的类型,以便问题不存在。具体做法无法确定,因为这里没有足够的上下文来了解您尝试做什么。
不过,以下是在没有泛型的情况下(您似乎也没有使用),可能看起来像什么:
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::os::unix::io::{RawFd, AsRawFd};

pub trait HRecv {}
pub trait HSend {}
pub trait HStream: HRecv + HSend + AsRawFd + CloneHStream {}

pub trait CloneHStream { fn clone_h_stream(&self) -> Box<HStream>; }

impl<T> CloneHStream for T where T: 'static + Clone + HStream {
    fn clone_h_stream(&self) -> Box<HStream> {
        Box::new(self.clone())
    }
}

pub struct Stream {
    pub inner: Box<HStream>
}

pub type StreamList = Arc<Mutex<LinkedList<Stream>>>;

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream>::new()));
}

5

您不能直接使用HStream类型;它不代表任何东西。它只用于构造派生指针类型,例如&HStreamBox<HStream>

最简单的解决方案是使用LinkedList存储Stream<Box<HStream>>

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream<Box<HStream>>>::new()));
}

那么,我们只需要为 Box<HStream> 实现 HStream

impl<'a> HRecv for Box<HStream + 'a> {}
impl<'a> HSend for Box<HStream + 'a> {}
impl<'a> AsRawFd for Box<HStream + 'a> {
    fn as_raw_fd(&self) -> RawFd { (&**self).as_raw_fd() }
}
impl<'a> HStream for Box<HStream + 'a> {}

请注意,这里缺少一个特性:CloneClone 不是对象安全的,这意味着无法为该特性创建 trait 对象类型,例如 &CloneBox<Clone>Clone 不是对象安全的,因为它的 clone 方法返回 Self,表示实现者的具体类型。如果您通过 trait 对象使用此方法,则编译器无法预先知道结果的类型(它可能是任何 Clone 的实现者!)。
由于 HStreamClone 的子特性,因此 HStream 也不是对象安全的。其结果是我们无法完全实现 Clone,并且像 Box<HStream> 这样的类型是不合法的。
但是,我们可以通过创建自己的对象安全特性来解决这个问题。我们甚至可以在实现标准 Clone 特性的类型上自动实现它。
pub trait BoxedHStreamClone {
    fn boxed_clone(&self) -> Box<HStream>;
}

// Implementation for all types that implement HStream and Clone and don't hold any borrows
impl<T: HStream + Clone + 'static> BoxedHStreamClone for T {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new(self.clone()) as Box<HStream>
    }
}

// Implementation for Box<HStream + 'a>, which cannot implement Clone
impl<'a> BoxedHStreamClone for Box<HStream + 'a> {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new((&**self).boxed_clone()) as Box<HStream>
    }
}

HStream 上的 Clone trait 约束替换为 BoxedHStreamClone,就可以开始使用了!

pub trait HStream: HRecv + HSend + AsRawFd + BoxedHStreamClone {}

Here's the final code:

use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::os::unix::io::{RawFd, AsRawFd};

pub trait BoxedHStreamClone {
    fn boxed_clone(&self) -> Box<HStream>;
}

impl<T: HStream + Clone + 'static> BoxedHStreamClone for T {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new(self.clone()) as Box<HStream>
    }
}

pub trait HRecv {}
pub trait HSend {}
pub trait HStream: HRecv + HSend + AsRawFd + BoxedHStreamClone {}
pub struct Stream<T: HStream> {
    pub inner: T
}

pub type StreamList = Arc<Mutex<LinkedList<Stream<Box<HStream>>>>>;

impl<'a> HRecv for Box<HStream + 'a> {}
impl<'a> HSend for Box<HStream + 'a> {}

impl<'a> AsRawFd for Box<HStream + 'a> {
    fn as_raw_fd(&self) -> RawFd { (&**self).as_raw_fd() }
}

impl<'a> BoxedHStreamClone for Box<HStream + 'a> {
    fn boxed_clone(&self) -> Box<HStream> {
        Box::new((&**self).boxed_clone()) as Box<HStream>
    }
}

impl<'a> HStream for Box<HStream + 'a> {}

fn main() {
    let mut list = Arc::new(Mutex::new(LinkedList::<Stream<Box<HStream>>>::new()));
}

感谢您抽出时间回答问题,两个答案得出了相同的结论,所以我选择了先提交的那个。再次感谢 :) - nathansizemore

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接