代码之家  ›  专栏  ›  技术社区  ›  user31601

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

  •  6
  • user31601  · 技术社区  · 7 年前

    我试图定义一个函数,它将引用作为参数,并调用 通用 方法,传入具体值。我需要一种方法来要求传递给函数的参数的泛型类型是函数将使用的具体类型的特征。我好像不知道怎么做。

    一个我想要达到的目标的最小例子:

    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{}));
    }
    

    尝试编译时会出现以下错误:

    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>>`
    

    我没想到这会编译,但我不知道如何更改的签名 populate 所以会的。我来自java land,在那里我将使用有界通配符(例如 void populate(List<? super Boat> receiver) ,但我找不到任何东西表明rust提供了等效的语义。

    我该如何修正我的定义 填充 在这里?

    我是新来的锈,所以忍受我,如果我完全是在找错树。我到处找了找,似乎找不到这个模式应该如何实现的示例。

    1 回复  |  直到 7 年前
        1
  •  5
  •   Shepmaster Tim Diekmann    6 年前

    稳定锈

    您可以为您感兴趣的每个独特的trait对象创建和实现trait:

    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));
    }
    

    宏可以删除重复项。

    夜锈

    你可以用不稳定的 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>);
    }
    

    相当于:

    #![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>);
    }
    

    你可以追踪他们的稳定情况 issue 27732 是的。

    这个代码是 只有 能够创建trait对象,但不能直接返回结构:

    let mut b: Vec<Box<Boat>> = vec![];
    populate(&mut b);
    
    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 | |     }
       | |_____^
    

    为了解决这个问题,您可以创建一个trait,就像我们对stable rust所做的那样,但是这个trait可以对所有trait对象都有一个全面的实现:

    #![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));
    }
    

    多亏了 aturon for pointing me to these traits to eddyb for reminding me that traits exist 啊!