的Index
的definition是:
pub trait Index<Index: ?Sized> {
type Output: ?Sized;
/// The method for the indexing (`Foo[Bar]`) operation
fn index<'a>(&'a self, index: &Index) -> &'a Self::Output;
}
具體地說,index
基準返回到其中參考具有相同的壽命作爲self
的元件。也就是說,它借了self
。
在你的情況下,index
呼叫self
(可能是&self.items[idx]
BTW)是self.items
,所以編譯器看到的是,返回值被限制在從self.items
借來的,但items
由next
的資self
,所以self.items
的借款是self
本身的借款。
也就是說,編譯器只能保證index
返回值是有效的,只要self
生命(約突變種種顧慮),所以&mut self
和壽命返回&...
要拉上。
如果一個編譯它,看到的錯誤,鏈接引用是編譯器的建議是什麼:
<anon>:23:29: 23:40 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
<anon>:23 Some(self.items.index(&idx))
^~~~~~~~~~~
<anon>:17:5: 25:6 help: consider using an explicit lifetime parameter as shown: fn next(&'a mut self) -> Option<&'a <I as Index<uint>>::Output>
<anon>:17 fn next(&mut self) -> Option<&'a <I as Index<uint>>::Output> {
<anon>:18 if (self.current_idx >= self.len) {
<anon>:19 None
<anon>:20 } else {
<anon>:21 let idx = self.current_idx;
<anon>:22 self.current_idx += self.stride;
...
但是,建議簽名fn next(&'a mut self) -> Option<&'a <I as Index<uint>>::Output>
比Iterator
性狀特徵更加嚴格,因此是非法的。 (與壽命的這種佈置迭代器可能是有用的,但他們沒有與許多普通消費者的工作,像.collect
。)
編譯器是一種防止被像一個類型表現出的問題:
struct IndexablePair<T> {
x: T, y: T
}
impl Index<uint> for IndexablePair<T> {
type Output = T;
fn index(&self, index: &uint) -> &T {
match *index {
0 => &self.x,
1 => &self.y,
_ => panic!("out of bounds")
}
}
}
這將兩個T
在線存儲(例如直接堆疊)並允許將它們編入索引pair[0]
和pair[1]
。 index
方法將指針直接返回到該存儲器(例如堆棧),所以如果IndexablePair
值在存儲器中移動,則這些指針將變爲無效,例如, (假設爲Stride::new(items: I, len: uint, stride: uint)
):
let pair = IndexablePair { x: "foo".to_string(), y: "bar".to_string() };
let mut stride = Stride::new(pair, 2, 1);
let value = stride.next();
// allocate some memory and move stride into, changing its address
let mut moved = box stride;
println!("value is {}", value);
倒數第二行是壞的!它使value
無效,因爲stride
和它的字段items
(對)在內存中移動,因此value
中的引用將指向移動的數據;這是非常不安全和非常糟糕的。
建議的生命週期通過借用stride
並禁止此舉來阻止此問題(以及其他一些有問題的問題),但正如我們上面所看到的,我們不能使用它。
到可熔酚醛這方面的一個技術是分離所述存儲器存儲從所述迭代器本身的元件,也就是改變它的Stride
的定義:(添加到items
的引用)
pub struct Stride<'a, I: Index<uint> + 'a> {
items: &'a I,
len: uint,
current_idx: uint,
stride: uint,
}
然後,編譯器有保證內存存儲單元是獨立於Stride
值(也就是,在內存中移動Stride
不會廢止舊的元素),因爲有一個不擁有指針把它們分開。該版本編譯罰款:
use std::ops::Index;
#[derive(Clone)]
pub struct Stride<'a, I: Index<uint> + 'a> {
items: &'a I,
len: uint,
current_idx: uint,
stride: uint,
}
impl<'a, I> Iterator for Stride<'a, I> where I: Index<uint> {
type Item = &'a <I as Index<uint>>::Output;
#[inline]
fn next(&mut self) -> Option<&'a <I as Index<uint>>::Output> {
if (self.current_idx >= self.len) {
None
} else {
let idx = self.current_idx;
self.current_idx += self.stride;
Some(self.items.index(&idx))
}
}
}
(理論上人們可以通過手動實現Clone
而不是derive
荷蘭國際集團將其添加在那裏結合的?Sized
,很可能使Stride
可以直接與&[T]
使用,即Stride::new(items: &I, ...)
Stride::new(&[1, 2, 3], ...)
會工作,而不是必須具有Stride::new(&&[1, 2, 3], ...)
雙層作爲結合的默認Sized
需要。)
playpen