【问题标题】:Lifetime in rust and old iterator pattern生锈和旧迭代器模式的生命周期
【发布时间】:2014-09-03 22:36:25
【问题描述】:

我发现 Rust 破坏了一些旧代码,我决心修复它。可悲的是,生命周期引用似乎发生了很大变化,并且正在发生一些奇怪的事情。 字段Counter.data 无关紧要,它只是用来表明我使用了一些数据并且它引用了通用的东西。

代码如下:

struct Counter <'c, T: 'c> {
    val: u32,
    data: &'c T
}

struct CounterIterator<'c, T:'c> {
    iter: &'c mut Counter<'c, T>,
    num: u32
}

impl<'r, T> Iterator<u32> for CounterIterator<'r, T> {
    fn next(&mut self) -> Option<u32> {
        if self.num  == 0 {
            None
        } else {
            self.num -= 1;
            self.iter.incr()
        }
    }
}

impl<'c, T> Counter <'c, T> {
    fn new(dataz: &'c T) -> Counter<'c, T> {
        Counter {
            val: 0u32,
            data: dataz
        }
    }

    fn incr(&mut self) -> Option<u32> {
        self.val += 1;
        println!("{}", self.val);
        Some(self.val)
    }

    fn iter(&'c mut self, num: u32) -> CounterIterator<'c, T> {
        CounterIterator {
            iter: self,
            num: num
        }
    }
}

fn main() {
    let val = 11u;
    let mut cnt = Counter::new(&val);
    for i in range(0,2u) {
        cnt.incr();
    }

    for i in cnt.iter(3) {

    }

    cnt.incr(); // <- commenting this out "fixes" the problem
    // Otherwise the error is 
    // note: previous borrow of `cnt` occurs here; the mutable borrow prevents 
    // subsequent moves, borrows, or modification of `cnt` until the borrow ends

}

这里有什么错误? 我该如何做到这一点,以便“迭代器”习语在它退出循环时结束借用,而不是在它定义的块结束时结束? 另外,显式生命周期T:'c 是做什么的?

作为记录,我正在尝试实现类似于 str.chars() 的迭代器 API。如果有更明智的方法,请告诉我。

【问题讨论】:

    标签: rust lifetime


    【解决方案1】:

    在fn iter 中,&amp;'c mut self 将self 的可变借用与Counter 的生命周期'c 联系起来。当您致电 cnt.incr() 时,您无法借用 cnt,因为.... cnt 还活着(所以 &amp;'c mut self 仍在借用它)。

    使您的代码工作的一种可能方法是将data 移动到Counter,而不是存储对它的借用引用,如下所示:

    struct Counter <T> {
        val: u32,
        data: T
    }
    

    如果您想保留数据作为参考,另一种选择是向CounterIterator 结构引入第二个命名生命周期,以便Counter 的可变借用可以比Counter 本身更短。

    // we now have a shorter lifetime ('s) and a longer one ('l). This is 
    // expressed to the compiler by the 'l: 's bound that says "'l lives 
    // at least as long as 's"
    struct CounterIterator<'s, 'l: 's, T: 'l> {
        iter: &'s mut Counter<'l, T>,
        num: u32
    }
    
    impl<'c,  T> Counter <'c, T> {
    // ...
        // now the &mut self can live as long as CounterIterator, not necessarily as
        // long as Counter. This allows it to be released as soon as the iteration
        // is over and CounterIterator goes out of scope
        fn iter<'a>(&'a mut self, num: u32) -> CounterIterator<'a,'c, T> {
            CounterIterator {
                iter: self,
                num: num
            }
        }
    }
    

    供参考,因为语言仍然有些变化:

    $ rustc -v
    rustc 0.12.0-pre-nightly (2e3858179 2014-09-03 00:51:00 +0000)
    

    【讨论】:

    • 只是一点评论。即使我没有使用这个解决方案,它也是几个正确的解决方案之一。其他解决方案包括通过使用 Cell 或等到借用检查器支持非词法范围来降低可变性。
    • @DanielFath 是的,还有其他方法。还有一个你定义 CounterIterator 有两个生命周期的地方(但它有点复杂):is.gd/eMsLJd
    • 实际上,这就是我正在寻找的解决方案。我只是从未设法使生命周期正常工作。您能否添加它,以免它被 pastebin/playpen 意外删除?
    猜你喜欢
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 2015-11-15
    • 2010-10-20
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多