在实现 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
struct 但似乎也被分配了一个'static
寿命。