Calling functions which return different types wit

2019-08-27 13:37发布

问题:

I'm still fairly new to rust. I would like to use a variable to choose between one of several functions which return structs of different types, but all of which have the same trait implemented. I would then like to pass the returned struct from the selected function to some functions which are meant to accept any variable which has that trait. However, I can't figure out how to do it. I've read How do I overcome match arms with incompatible types for structs implementing same trait? but I'm missing something because I still can't get it to work. The function which I pass the returned value to doesn't accept the value - see below.

Here's a simplified example using one of the methods from the above link:

trait IsEven {
    fn is_even(&self) -> bool;
}

struct First {
    v: u8,
}

impl IsEven for First {
    fn is_even(&self) -> bool {
        self.v % 2 == 0
    }
}

struct Second {
    v: Vec<u8>,
}

impl IsEven for Second {
    fn is_even(&self) -> bool {
        self.v[0] % 2 == 0
    }
}

fn make1() -> First {
    First{v: 5}
}

fn make2() -> Second {
    Second{v: vec![2, 3, 5]}
}


fn requires_is_even(v: impl IsEven) {
    println!("{:?}", v.is_even());
}

fn main() {
    for i in 0..2 {
        let v1;
        let v2;
        let v = match i {
            0 => {
                v1 = make1();
                &v1 as &IsEven
            }
            _ => {
                v2 = make2();
                &v2 as &IsEven
            }
        };
        requires_is_even(v);  // This is where it fails
    }
}

The error I get in this case is:

52 |         requires_is_even(v);
   |         ^^^^^^^^^^^^^^^^ the trait `IsEven` is not implemented for `&dyn IsEven`

I've also tried using Box as in some of the other examples in the link above, but still can't get it to work. Can anyone help?

Thanks

Bob

回答1:

requires_is_even, as you've written it, receives an object that implements IsEven by value, although all methods on the trait take self by shared reference. However, despite that, &dyn IsEven doesn't automatically implement IsEven (though we can add that implementation ourselves, see below).

You have a few options here:

  1. Change the function to receive an object implementing IsEven by shared reference. (This version does static dispatch.)

    fn requires_is_even(v: &(impl IsEven + ?Sized)) {
         println!("{:?}", v.is_even());
    }
    

    Note: The ?Sized bound is necessary here because impl Trait in argument position is syntactic sugar for a type parameter, and type parameters have an implicit Sized bound.

  2. Change the function to receive an IsEven trait object by shared reference. (This version does dynamic dispatch.)

    fn requires_is_even(v: &dyn IsEven) {
        println!("{:?}", v.is_even());
    }
    
  3. Implement IsEven for any shared reference to a type that implements IsEven.

    impl<T> IsEven for &T
    where
        T: IsEven + ?Sized
    {
        fn is_even(&self) -> bool {
            (**self).is_even()
        }
    }
    

    Note: By adding the ?Sized bound, this impl applies to &dyn IsEven as well. Trait objects (dyn IsEven here, not &dyn IsEven or Box<dyn IsEven>) automatically implement their corresponding trait (if the trait is object-safe, otherwise the trait object type isn't usable at all, by definition).



标签: rust