繁体   English   中英

函数vs struct的类型参数(生命期问题)

[英]type parameter for function vs struct (lifetime issue)

考虑以下测试用例:

#![allow(unstable)]
trait Choose<'o> { 
    fn choose(a: &'o u64, b: &'o u32) -> Self; 
}

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

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

struct Handler {
    a: u64,
    b: u32,
}

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

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

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

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

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

围栏

假设我在Handler :: find中有一些改变状态,所以我需要&mut self 但是指向Handler内部的v_av_b变量都存在于自己的块中,因此这里没有借用问题。 在这种情况下,直接为find方法指定类型参数V ,并且所有内容都按预期编译。

不过,接我一招参数V输入处理程序类型签名,并停止与编译“不能借h为可变不止一次同时更”错误:

#![allow(unstable)]
trait Choose<'o> { 
    fn choose(a: &'o u64, b: &'o u32) -> Self; 
}

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

impl<'o> Choose<'o> for &'o 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<V> where V: Choose<'a>, W: PartialEq<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);
    }
}

围栏

我真的无法理解其中的区别。 变量v_a死后,为什么不发布可变借用?

我认为这里发生的事情是:在你的main ,当你做let mut h = Handler::<&u64>::new(); ,你的处理程序现在与u64引用u64 因此,即使v_a在下一个块中死亡,V的生命周期必须是h的生命周期,它仍然存在。

顺便说一句,问题不在于你已编写的代码,而是代码中你或其他人仍然可以编写。 鉴于您使用不受约束的V定义Handler,我可以继续执行:

// 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 }
}

然后这是合法的:

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

所以,每当我做let h = Handler::<&u64>::new(); 和你一样,唯一安全的选择是让&64至少和h一样长。

如果你可以使用u64作为V ,而不是&u64你会没事的。 像这样的东西会很少改变你的程序(注意我仍在使用引用,而不是通过值传递),但允许你为u32 / 64而不是&u32 / 64参数化Handler:

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

围栏

这是我对问题的理解,其他人可能能够提供更具体的解释。

通过将类型参数添加到结构中,您可以将该类型存储在结构中 既然你还指定你的类型具有特征Choose<'a> 并且 'aself的生命周期相关联,那么当你创建函数时,Rust必须假设你可能会在结构中存储一个(可变的)引用呼叫。 然后编译器必须将您的可变借位转移到该函数,并且它不知道它何时结束。 唯一安全的时间是对象本身超出范围

这是存储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
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM