如何使用可以是多种特征对象的参数定义函数?

use*_*601 6 generics boxing traits rust

我正在尝试定义一个函数,它将引用作为参数,并在引用的对象上调用泛型方法,传递一个具体的值.我需要一种方法来要求传递给我的函数的泛型类型是函数将使用它的具体类型的特征.我似乎无法弄清楚如何做到这一点.

我试图实现的那种事情的最小例子:

trait Vehicle {}
trait Floating {}

struct Boat;
impl Vehicle for Boat {}
impl Floating for Boat {}

fn main() {
    let mut a: Vec<Box<Vehicle>> = vec![];
    populate(&mut a); // Does not compile

    let mut b: Vec<Box<Floating>> = vec![];
    populate(&mut b); // Also does not compile
}

fn populate(receiver: &mut Vec<Box<Boat>>) { // What should I put here?
    receiver.push(Box::new(Boat{}));
}
Run Code Online (Sandbox Code Playgroud)

尝试编译它会产生以下错误:

error[E0308]: mismatched types
  --> src/main.rs:10:14
   |
10 |     populate(&mut a); // Does not compile
   |              ^^^^^^ expected struct `Boat`, found trait Vehicle
   |
   = note: expected type `&mut std::vec::Vec<std::boxed::Box<Boat>>`
              found type `&mut std::vec::Vec<std::boxed::Box<Vehicle>>`


error[E0308]: mismatched types
  --> src/main.rs:13:14
   |
13 |     populate(&mut b); // Also does not compile
   |              ^^^^^^ expected struct `Boat`, found trait Floating
   |
   = note: expected type `&mut std::vec::Vec<std::boxed::Box<Boat>>`
              found type `&mut std::vec::Vec<std::boxed::Box<Floating>>`
Run Code Online (Sandbox Code Playgroud)

我没想到这会编译,但我不知道如何改变它的签名populate.我来自Java土地,在那里我将使用有限的通配符(例如void populate(List<? super Boat> receiver))来实现这一点,但我找不到任何暗示Rust提供等效语义的东西.

我怎么能修复我在populate这里的定义?

我是Rust的新手,如果我完全咆哮错误的树,请耐心等待.我一直在搜索,似乎无法找到如何实现这种模式的例子.

She*_*ter 5

稳定的锈

您可以为您感兴趣的每个唯一特征对象创建和实现特征:

trait Shipyard {
    fn construct(boat: Boat) -> Box<Self>;
}

impl Shipyard for Boat {
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat)
    }
}

impl Shipyard for Vehicle {
    fn construct(boat: Boat) -> Box<Vehicle> {
        Box::new(boat) as Box<Vehicle>
    }
}

impl Shipyard for Floating {
    fn construct(boat: Boat) -> Box<Floating> {
        Box::new(boat) as Box<Floating>
    }
}

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    T: Shipyard,
{
    receiver.push(T::construct(Boat));
}
Run Code Online (Sandbox Code Playgroud)

宏可以删除重复.

夜间生锈

你可以使用不稳定的CoerceUnsized特质:

#![feature(coerce_unsized)]

use std::ops::CoerceUnsized;

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    Box<Boat>: CoerceUnsized<Box<T>>,
{
    receiver.push(Box::new(Boat) as Box<T>);
}
Run Code Online (Sandbox Code Playgroud)

等价的:

#![feature(unsize)]

use std::marker::Unsize;

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    Boat: Unsize<T>,
{
    receiver.push(Box::new(Boat) as Box<T>);
}
Run Code Online (Sandbox Code Playgroud)

您可以在问题27732中跟踪其稳定性.

此代码只能创建特征对象,并且不能直接返回结构:

let mut b: Vec<Box<Boat>> = vec![];
populate(&mut b);
Run Code Online (Sandbox Code Playgroud)
error[E0277]: the trait bound `Boat: std::marker::Unsize<Boat>` is not satisfied
  --> src/main.rs:17:5
   |
17 |     populate(&mut b);
   |     ^^^^^^^^ the trait `std::marker::Unsize<Boat>` is not implemented for `Boat`
   |
   = note: required because of the requirements on the impl of `std::ops::CoerceUnsized<std::boxed::Box<Boat>>` for `std::boxed::Box<Boat>`
note: required by `populate`
  --> src/main.rs:25:5
   |
25 | /     fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
26 | |     where
27 | |         Box<Boat>: CoerceUnsized<Box<T>>,
28 | |     {
29 | |         receiver.push(Box::new(Boat) as Box<T>);
30 | |     }
   | |_____^
Run Code Online (Sandbox Code Playgroud)

要解决这个问题,你可以像我们为稳定的Rust一样创建一个特性,但是这个特性可以为所有特征对象提供一个全面的实现:

#![feature(unsize)]

use std::marker::Unsize;

trait Shipyard {
    fn construct(boat: Boat) -> Box<Self>;
}

impl Shipyard for Boat {
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat)
    }
}

impl<U: ?Sized> Shipyard for U
where
    Boat: Unsize<U>,
{
    fn construct(boat: Boat) -> Box<Self> {
        Box::new(boat) as Box<U>
    }
}

fn populate<T: ?Sized>(receiver: &mut Vec<Box<T>>)
where
    T: Shipyard,
{
    receiver.push(T::construct(Boat));
}
Run Code Online (Sandbox Code Playgroud)

感谢aturon指出这些特性,感谢eddyb提醒我存在特征!