type parameter for function vs struct (lifetime issue)

坚强是说给别人听的谎言 提交于 2019-12-05 22:34:47

I think what is happening here is: in your main, when you do let mut h = Handler::<&u64>::new();, your Handler is now tied to the lifetime of that reference to u64. So even if v_a dies in the following block, the lifetime of V must be that of h, which is still alive.

The problem, by the way, is not so much in the code you already wrote, but in the code you or somebody else could still write. Given your definition of Handler with an unconstrained V, I could go ahead and do:

// in the meanwhile, in another crate...
// I create another trait
trait MyTrait {
    fn foo(&self) -> &u64;
}

// and implement it for Handler<&u64>
impl<'a> MyTrait for Handler<&'a u64> {
    fn foo(&self) -> &u64 { &self.a }
}

and then this would be legal:

let h = Handler::<&u64>::new();    
println!("{}", h.foo()); // prints 14

So, whenever I do let h = Handler::<&u64>::new(); like you did, the only safe option is for the &64 to live at least as long as h.

If you could use u64 as V, instead of &u64 you would be fine. Something like this changes your program very little (note that I'm still working with references, not passing by value), but allows you to parametrize Handler for u32/64 instead of &u32/64 :

trait Choose<'o> { 
    fn choose(a: &'o u64, b: &'o u32) -> &'o Self; 
}

impl<'o> Choose<'o> for u64 { 
    fn choose(a: &'o u64, _b: &'o u32) -> &'o u64 { a }
}

impl<'o> Choose<'o> for u32 { 
    fn choose(_a: &'o u64, b: &'o u32) -> &'o u32 { b }
}

struct Handler<V> {
    a: u64,
    b: u32,
}

impl<V> Handler<V> {
    fn new() -> Handler<V> {
        Handler { a: 14, b: 15 }
    }

    fn find<'a, W>(&'a mut self, value: W) -> Option<&'a V> where V: Choose<'a>, W: PartialEq<&'a V> {
        let v = Choose::choose(&self.a, &self.b);
        if value == v {
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    let mut h = Handler::<u64>::new();

    {
        let v_a = h.find(&14u64);
        println!("v_a = {:?}", v_a);
    }

    {
        let v_b = h.find(&15u64);
        println!("v_b = {:?}", v_b);
    }
}

playpen

Here's my understanding of the problem, others may be able to provide more concrete explanations.

By adding the type parameter to your struct, you are able to store that type in the struct. Since you also specify that your type has the trait Choose<'a> and 'a is tied to the lifetime of self, Rust has to assume that you are potentially going to store a (mutable) reference to the struct when you make the function call. The compiler must then transfer your mutable borrow to the function, and it doesn't know when it ends. The only safe time is when the object itself goes out of scope

Here's an example of storing a V:

fn find<'a, W>(&'a mut self, value: W) -> Option<V> where V: Choose<'a>, W: PartialEq<V> { //'
    let v = Choose::choose(&self.a, &self.b);

    self.c = Some(Choose::choose(&self.a, &self.b)); // saved

    if value == v {
        Some(v)
    } else {
        None
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!