在实现 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/