rust - 我是否错误地实现了 IntoIterator 以引用 LazyList 实现,或者这是一个 Rust 错误?

标签 rust lifetime borrow-checker borrowing lifetime-scoping

在实现 LazyList 的一个版本(一个不可变的延迟计算的记忆化单链表,很像 Haskell 列表)时,我遇到了实现 IntoIterator 的问题。因为代码不会在我认为应该删除引用时删除引用。以下代码已被简化,以便仅显示问题;因此,它不是通用的,并且不包括与实现 IntoIterator 无关的所有方法。 :

use std::cell::UnsafeCell;
use std::mem::replace;
use std::rc::Rc;

// only necessary because Box<FnOnce() -> R> doesn't yet work...
trait Invoke<R = ()> {
    fn invoke(self: Box<Self>) -> R;
}

impl<'a, R, F: 'a + FnOnce() -> R> Invoke<R> for F {
    #[inline(always)]
    fn invoke(self: Box<F>) -> R {
        (*self)()
    }
}

// not thread safe
struct Lazy<'a, T: 'a>(UnsafeCell<LazyState<'a, T>>);

enum LazyState<'a, T: 'a> {
    Unevaluated(Box<Invoke<T> + 'a>),
    EvaluationInProgress,
    Evaluated(T),
}

use self::LazyState::*;

impl<'a, T: 'a> Lazy<'a, T> {
    #[inline]
    fn new<F: 'a + FnOnce() -> T>(func: F) -> Lazy<'a, T> {
        Lazy(UnsafeCell::new(Unevaluated(Box::new(func))))
    }
    #[inline]
    pub fn evaluated(val: T) -> Lazy<'a, T> {
        Lazy(UnsafeCell::new(Evaluated(val)))
    }
    #[inline]
    fn value(&'a self) -> &'a T {
        unsafe {
            match *self.0.get() {
                Evaluated(_) => (), // nothing required; already Evaluated
                EvaluationInProgress => panic!("Lazy::force called recursively!!!"),
                _ => {
                    let ue = replace(&mut *self.0.get(), EvaluationInProgress);
                    if let Unevaluated(thnk) = ue {
                        *self.0.get() = Evaluated(thnk.invoke());
                    } // no other possiblity!
                }
            } // following just gets evaluated, no other state possible
            if let Evaluated(ref v) = *self.0.get() {
                return v;
            } else {
                unreachable!();
            }
        }
    }
}

enum LazyList<'a> {
    Empty,
    Cons(i32, RcLazyListNode<'a>),
}

type RcLazyListNode<'a> = Rc<Lazy<'a, LazyList<'a>>>;

impl<'a> LazyList<'a> {
    fn iter(&self) -> Iter<'a> {
        Iter(self)
    }
}

struct Iter<'a>(*const LazyList<'a>);

impl<'a> Iterator for Iter<'a> {
    type Item = &'a i32;

    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            if let LazyList::Cons(ref v, ref r) = *self.0 {
                self.0 = r.value();
                Some(v)
            } else {
                None
            }
        }
    }
}

impl<'a> IntoIterator for &'a LazyList<'a> {
    type Item = &'a i32;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

fn main() {
    let test2 = LazyList::Cons(2, Rc::new(Lazy::evaluated(LazyList::Empty)));
    let test = LazyList::Cons(1, Rc::new(Lazy::new(move || test2)));
    // let itr = Iter(&test); // works
    // let itr = (&test).iter(); // works
    let itr = IntoIterator::into_iter(&test); // not working
    for v in itr {
        println!("{}", v);
    }
}

上面的代码失败了:

rustc 1.13.0 (2c6933acc 2016-11-07)
error: `test` does not live long enough
   --> <anon>:103:40
    |
103 |     let itr = IntoIterator::into_iter(&test); // not working
    |                                        ^^^^ does not live long enough
...
107 | }
    | - borrowed value dropped before borrower
    |
    = note: values in a scope are dropped in the opposite order they are created

main() 中的评论所述,代码可用除非通过 IntoIterator 特性作为引用调用。这可能是为引用实现特征时的一个错误,其中包含指针的返回迭代器的所有权未转移到与调用 IntoIterator::into_iter 相同的范围。而是到 'static生命周期,因此,它不会在预期时被丢弃。

如果可能,我该如何实现?我试过添加 std::marker::PhantomData<>标记字段到 Iter结构,但似乎也被分配了一个 'static生命周期。

最佳答案

当您实现 IntoIterator 时,您统一了对列表的引用和列表包含的项目之间的生命周期:

impl<'a> IntoIterator for &'a LazyList<'a>

这要求 'a 必须是较短的生命周期。这在这种情况下没有用。相反,您需要有两个不同的生命周期:

impl<'l, 'i> IntoIterator for &'l LazyList<'i> {
    type Item = &'i i32;
    type IntoIter = Iter<'i>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

关于rust - 我是否错误地实现了 IntoIterator 以引用 LazyList 实现,或者这是一个 Rust 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40687955/

相关文章:

error-handling - 如果成功,从没有结果的函数返回错误的惯用方法是什么?

Rust 子类型化/"runtime polymorphism"/在 Boxes 中使用特征

struct - 如何为&Struct实现Default?

rust - 如何将 C 可变长度数组代码转换为 Rust?

rust - 在容器之后创建借用值时,如何添加对容器的引用?

rust - "cannot find macro"宏自己的doc测试出错

rust - 根据索引替换数组元素

windows-phone-7 - WP7 : Push Notification Subscription Lifetime?

rust - 如何使用通用 VecDeque?

rust - 如何处理 HashMap 中的每个值并选择性地拒绝一些值?