Generics 这个代码可以不用泛型编写吗?

Generics 这个代码可以不用泛型编写吗?,generics,rust,Generics,Rust,我有这个结构: #[deriving(Clone)] pub struct MiddlewareStack { handlers: Vec<Box<Middleware + Send>> } 这很好,但我想知道,为什么必须使用泛型呢 所以我试了一下: pub fn utilize(&mut self, handler: Middleware){ self.middleware_stack.add(handler); } 但这给我留下了一个错误:

我有这个结构:

#[deriving(Clone)]
pub struct MiddlewareStack {
    handlers: Vec<Box<Middleware + Send>>
}
这很好,但我想知道,为什么必须使用泛型呢

所以我试了一下:

pub fn utilize(&mut self, handler: Middleware){
    self.middleware_stack.add(handler);
}
但这给我留下了一个错误:

error: reference to trait `Middleware` where a type is expected; try `Box<Middleware>` or `&Middleware`
       pub fn utilize(&mut self, handler: Middleware){
但是好的,
:中间件
绑定不满足于一个
,再想想,这实际上是有道理的。否则,无论如何,我都会在上面的代码中使用双重装箱

如果我更改为:

pub fn utilize(&mut self, handler: Box<Middleware + Send>){
    self.middleware_stack.add(handler);
}
它按预期工作。请注意,它们不在同一个模块中,因此是封装。但这意味着在调用方站点上,我必须将代码更改为
use(框中有一些中间件

使用通用实现,我能够在我的层的最底层调用
box
,也就是

pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
进入:

无论如何,在某个时刻


如果编译器在幕后或多或少都会这样做,为什么我不允许使用未绑定版本的
中间件
作为简单参数?

Rust目前提供了两种编写多态代码的方法:泛型和特征对象

泛型以类型参数的形式存在。也就是说,函数具有由调用方选择的附加参数。然后,编译器生成相应的函数单态版本,其中所有类型参数都替换为具体类型:

fn add<T: Add<T, Output=T>>(a: T, b: T) -> T {
    a + b
}

// when used like this:
let (a, b) = (1, 2);
let c = add(a, b);

// roughly the following code will be generated:
fn add(a: i32, b: i32) -> i32 {
    a + b
}
请注意,向量包含不同实际类型的元素,但它们都满足
Display
trait

但是,与泛型不同,trait对象对性能有影响。因为编译器不知道它们的具体类型,所以它应该使用vtables来查找要在它们上执行的方法。因为trait对象应该始终通过指针访问,所以您只能将它们保留在堆栈上,或者将它们装箱以存储在结构中。例如,不可能将“裸”特征对象保存到结构的字段中

此外,并非所有特征都能生成特征对象,或者换句话说,并非所有特征都能用于特征对象。例如,签名中有
Self
类型的trait方法不能用于trait对象。原因应该很明显:这些方法要求调用站点知道具体的实现者类型,而trait对象则不是这样

备注:实际上可以在结构中存储裸特征对象,尽管有一些限制。例如,您只能将一个裸露的trait对象存储为结构的最后一个字段,并且这种结构只能通过指针访问,因为它也会变为非大小。您可以阅读有关未调整大小的类型(或动态调整大小的类型,这些是同义词)的更多信息

我更想理解为什么它必须使用泛型,因为 它来自Java的C#等语言,可能只是一种非语言 将接口用作参数的泛型方法,该参数应 大致转化为锈病的特征

您可以从上面的所有内容中看到,泛型在绝大多数情况下都更加有用和高效。正因为如此,生锈促进了泛型而非特征对象的使用。所以,当你需要编写一个泛型函数时,你需要从泛型开始,只有在你真正需要的时候才转向trait对象。在这方面,Rust不同于Java或C#

然而,您的具体问题似乎在于您正在调用
中间件\u stack.add()
方法,根据错误消息,该方法似乎是通用的。应该是这样的:

pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
fn add<T: Middleware+Send>(&mut self, handler: T) { ... }
这将起作用,但仅当此方法在与
MiddlewareStack
结构相同的模块中定义时,因为
handlers
字段是私有的

对后续行动的回答

我不知道你为什么决定让编译器翻译

pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
这不是它的工作原理。上面的泛型版本在使用特定类型调用时是单形态的,这是我文章中的第一个示例所显示的。例如,如果您有用于SomeHandler的
impl中间件
,并且您调用了
self.add(SomeHandler{…})
,编译器将生成专门版本的
add()
方法:

pub fn add(&mut self, handler: SomeHandler) {
    self.handlers.push(box handler);
}
这应该是非常简单的

在对另一个答案的评论中回答最新的后续问题


最后一次跟进。在上面的例子中,你会喜欢泛型 实现优于非泛型实现,对吗?基本上 因为你不想把拳击一直“泄露”到球场 打电话的,对吗?至少对我来说,这是最烦人的事情。 我不想使用Box作为参数并强制 调用方利用(框中的某些\u中间件)。那更漂亮 使用不强制装箱的通用实现 往上走。这是核心动机吗

事实上,这只是一个动机。但我认为,最重要的一点是泛型更有效。我在上面说过:泛型函数的单形态化允许静态分派,即编译器确切地知道调用函数的哪个变体,并且可以基于这些知识应用优化,例如内联。对于trait对象,内联是不可能的,因为对trait对象的方法的所有调用都应该通过该对象的虚拟表进行


您也可以通过@dbaupp阅读(尽管这是对不同问题的回答)。只需将
Go
替换为
Java/C#
,您将得到大致相同的结果。

编译器不会翻译以下内容:

它将在编译时创建一个专用版本,并向其传递一个具体类型:

struct Foo;

impl Middleware for Foo { ... }

pub fn add (&mut self, handler: Foo) {
    self.handlers.push(box handler);
}

谢谢你非常详细的回答!我在问题中留下了一个后续问题。如果你能留下一些反馈,那就太棒了。@Christoph,我已经为你的后续行动添加了答案。它们基本上与阿扬的答案相同,不过:)太棒了!那些
fn add<T: Add<T, Output=T>>(a: T, b: T) -> T {
    a + b
}

// when used like this:
let (a, b) = (1, 2);
let c = add(a, b);

// roughly the following code will be generated:
fn add(a: i32, b: i32) -> i32 {
    a + b
}
fn show_all(v: &[&Display]) {
    for (i, item) in v.iter().enumerate() {
        println!("v[{}] = {}", i, item);
    }
}

let a = 10;
let b = "abcd";
let c = 0.9f64;
show_all(&[&a, &b, &c]);
fn add<T: Middleware+Send>(&mut self, handler: T) { ... }
impl Middleware for Box<Middleware> { ... }
pub fn utilize(&mut self, handler: Box<Middleware+Send>){
    self.middleware_stack.handlers.push(handler);
}
pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
pub fn add (&mut self, handler: Middleware) {
    self.handlers.push(box handler);
}
pub fn add(&mut self, handler: SomeHandler) {
    self.handlers.push(box handler);
}
pub fn add<T: Middleware> (&mut self, handler: T) {
    self.handlers.push(box handler);
}
pub fn add (&mut self, handler: Middleware) {
    self.handlers.push(box handler);
}
struct Foo;

impl Middleware for Foo { ... }

pub fn add (&mut self, handler: Foo) {
    self.handlers.push(box handler);
}