在具有多个生命周期约束的类型上实现索引
Implementing Index on a type with multiple lifetime constraints
我 运行 遇到了在具有生命周期约束的类型上实施 Index
的问题。我有一个 SubImage
结构,其中包含对 Image
的引用。我找不到任何方法来满足编译器。
impl<'a, P> Index<usize> for SubImage<'a, P> {
type Output = [P];
fn index<'b> (&'b self, y: usize) -> &'b [P] {
let start = (self.rect.y0 + y) * self.image.size.x + self.rect.x0;
let end = start + self.rect.width();
&self.image.buf[start..end]
}
}
'a
是引用图像的生命周期,因此切片其缓冲区需要此约束。此处的代码可以编译,但不明确。对索引运算符的所有调用都会产生一条错误消息,例如:
src/image.rs:179:13: 179:32 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
src/image.rs:179 Some(&self.sub[self.back])
^~~~~~~~~~~~~~~~~~~
src/image.rs:174:3: 181:4 help: consider using an explicit lifetime parameter as shown: fn next_back(&'a mut self) -> Option<&'a [P]>
src/image.rs:174 fn next_back (&mut self) -> Option<&'a [P]> {
src/image.rs:175 if self.front == self.back {
src/image.rs:176 None
src/image.rs:177 } else {
src/image.rs:178 self.back -= 1;
src/image.rs:179 Some(&self.sub[self.back])
是否有任何可能的方法来确保 return 值被限制为 'a
和 'b
或其他一些在某种情况下正确实施 Index
的方法像这样?编译器的建议不起作用,因为函数签名与特征的签名不匹配。
您的问题实际上不止于此,而且不是 Index 特征实现。此外,您的示例并不是真正的最小、完整和可验证的示例 (MCVE),因此我不得不猜测您的确切问题出在哪里。
你的问题的核心是,你不能让你的迭代器 return 引用,如果它拥有内容而不借用迭代器本身。您为您的 SubImage 实现 Index 特征很好。
我会尝试模拟你的问题。假设我们有一个 struct Julmond 并且它借用了一些整数片段(类似于您的 SubImage)。
struct Julmond<'a>(&'a [i32]);
impl<'a> Index<usize> for Julmond<'a> {
type Output = [i32];
fn index<'b>(&'b self, idx: usize) -> &'b [i32] {
if idx < self.0.len() {
&self.0[idx..] // we always take a subslice from idx until the end
} else {
panic!("Index out of bounds!")
}
}
}
Index trait 要求我们借用 self。这很好,因为一些实现者可能拥有您正在索引的数据。这种对 self 的借用是通过链接 self 的命名生命周期和特征 method signature:
中的传出引用来表达的
fn index(&'a self, index: Idx) -> &'a Self::Output;
如果我们对 Julmond 进行索引,只要我们保留对 Julmond 的结果引用,该值就被认为是借用的:
let array = [1, 2, 3, 4, 5, 6];
let mut j = Julmond(&array);
let r = &j[3];
&mut j; // Error: r is still in scope and therefore j is still borrowed
我可以从您的示例代码中了解到,您有一些类型拥有您的 SubImage 并实现了 Iterator 特性。我们将尝试用另一个实现 Iterator 特性的结构 Nebelung 来模仿它:
struct Nebelung<'a> {
j: Julmond<'a>,
pos: usize,
}
impl<'a> Iterator for Nebelung<'a> {
type Item = &'a [i32];
fn next(&mut self) -> Option<&'a [i32]> {
if self.pos < self.j.0.len() {
let tmp_pos = self.pos;
self.pos += 1;
Some(&self.j[tmp_pos]) // problematic line
} else {
None
}
}
}
此实现 return 是底层 Julmond 结构中不断缩小的数组切片。我们可以这样测试:
fn main() {
let array = [1, 2, 3, 4, 5, 6];
let j = Julmond(&array);
let n = Nebelung { j: &j, pos: 0 };
for s in n {
println!("{:?}", s);
}
}
但这行不通。编译器会抱怨(就像在你的例子中一样)它不能为 'a.原因是index方法中self的借用。当我们用 j[tmp_pos] 调用索引运算符时,我们是在借用 j。但是 j 由 Nebelung 类型的 self 拥有,因此从 j 借用意味着我们从 self 借用。我们正在尝试 return 对 self 拥有的东西的引用,这要求 self 也必须被借用。编译器提出了正确的建议:将 self 的生命周期与传出引用联系起来。但是,这违反了 next.
的方法签名
如果我们想要 return 来自迭代器的引用,该迭代器不能拥有 returned 值。否则,我们将不得不在调用中借用迭代器,但这在 next 中是不可能的。
解决这个问题的唯一方法是让迭代器不拥有该值。所以我们修改 Nebelung 结构以保存对 Julmond 的引用:
struct Nebelung<'a: 'b, 'b> {
j: &'b Julmond<'a>,
pos: usize,
}
'a:'b 表示“'a 比'b 长”,这里是必需的。因为我们对 Julmond 的引用 j 一定不能比 Julmond 的借用内容长。好吧,我们的 Nebelung 不再是 Julmond 的主人了。只是一个借款人。现在我们可以像这样为它实现 Iterator 特性:
impl<'a, 'b> Iterator for Nebelung<'a, 'b> {
type Item = &'b [i32];
fn next(&mut self) -> Option<&'b [i32]> {
if self.pos < self.j.0.len() {
let tmp_pos = self.pos;
self.pos += 1;
Some(&self.j[tmp_pos])
} else {
None
}
}
}
不需要链接 self 和传出引用的生命周期,因为我们只是 return引用一些我们不是其所有者的值。所以对 &self.j[tmp_pos] 的调用不再是从 self 借来的。它是从 Julmond 借来的(通过索引实现)。
无论您为哪种类型实现 Iterator 特性。如果类型拥有该值,则不能有下一个(或 next_back)return 引用。让您的类型借用 SubImage。
我 运行 遇到了在具有生命周期约束的类型上实施 Index
的问题。我有一个 SubImage
结构,其中包含对 Image
的引用。我找不到任何方法来满足编译器。
impl<'a, P> Index<usize> for SubImage<'a, P> {
type Output = [P];
fn index<'b> (&'b self, y: usize) -> &'b [P] {
let start = (self.rect.y0 + y) * self.image.size.x + self.rect.x0;
let end = start + self.rect.width();
&self.image.buf[start..end]
}
}
'a
是引用图像的生命周期,因此切片其缓冲区需要此约束。此处的代码可以编译,但不明确。对索引运算符的所有调用都会产生一条错误消息,例如:
src/image.rs:179:13: 179:32 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
src/image.rs:179 Some(&self.sub[self.back])
^~~~~~~~~~~~~~~~~~~
src/image.rs:174:3: 181:4 help: consider using an explicit lifetime parameter as shown: fn next_back(&'a mut self) -> Option<&'a [P]>
src/image.rs:174 fn next_back (&mut self) -> Option<&'a [P]> {
src/image.rs:175 if self.front == self.back {
src/image.rs:176 None
src/image.rs:177 } else {
src/image.rs:178 self.back -= 1;
src/image.rs:179 Some(&self.sub[self.back])
是否有任何可能的方法来确保 return 值被限制为 'a
和 'b
或其他一些在某种情况下正确实施 Index
的方法像这样?编译器的建议不起作用,因为函数签名与特征的签名不匹配。
您的问题实际上不止于此,而且不是 Index 特征实现。此外,您的示例并不是真正的最小、完整和可验证的示例 (MCVE),因此我不得不猜测您的确切问题出在哪里。
你的问题的核心是,你不能让你的迭代器 return 引用,如果它拥有内容而不借用迭代器本身。您为您的 SubImage 实现 Index 特征很好。
我会尝试模拟你的问题。假设我们有一个 struct Julmond 并且它借用了一些整数片段(类似于您的 SubImage)。
struct Julmond<'a>(&'a [i32]);
impl<'a> Index<usize> for Julmond<'a> {
type Output = [i32];
fn index<'b>(&'b self, idx: usize) -> &'b [i32] {
if idx < self.0.len() {
&self.0[idx..] // we always take a subslice from idx until the end
} else {
panic!("Index out of bounds!")
}
}
}
Index trait 要求我们借用 self。这很好,因为一些实现者可能拥有您正在索引的数据。这种对 self 的借用是通过链接 self 的命名生命周期和特征 method signature:
中的传出引用来表达的fn index(&'a self, index: Idx) -> &'a Self::Output;
如果我们对 Julmond 进行索引,只要我们保留对 Julmond 的结果引用,该值就被认为是借用的:
let array = [1, 2, 3, 4, 5, 6];
let mut j = Julmond(&array);
let r = &j[3];
&mut j; // Error: r is still in scope and therefore j is still borrowed
我可以从您的示例代码中了解到,您有一些类型拥有您的 SubImage 并实现了 Iterator 特性。我们将尝试用另一个实现 Iterator 特性的结构 Nebelung 来模仿它:
struct Nebelung<'a> {
j: Julmond<'a>,
pos: usize,
}
impl<'a> Iterator for Nebelung<'a> {
type Item = &'a [i32];
fn next(&mut self) -> Option<&'a [i32]> {
if self.pos < self.j.0.len() {
let tmp_pos = self.pos;
self.pos += 1;
Some(&self.j[tmp_pos]) // problematic line
} else {
None
}
}
}
此实现 return 是底层 Julmond 结构中不断缩小的数组切片。我们可以这样测试:
fn main() {
let array = [1, 2, 3, 4, 5, 6];
let j = Julmond(&array);
let n = Nebelung { j: &j, pos: 0 };
for s in n {
println!("{:?}", s);
}
}
但这行不通。编译器会抱怨(就像在你的例子中一样)它不能为 'a.原因是index方法中self的借用。当我们用 j[tmp_pos] 调用索引运算符时,我们是在借用 j。但是 j 由 Nebelung 类型的 self 拥有,因此从 j 借用意味着我们从 self 借用。我们正在尝试 return 对 self 拥有的东西的引用,这要求 self 也必须被借用。编译器提出了正确的建议:将 self 的生命周期与传出引用联系起来。但是,这违反了 next.
的方法签名如果我们想要 return 来自迭代器的引用,该迭代器不能拥有 returned 值。否则,我们将不得不在调用中借用迭代器,但这在 next 中是不可能的。
解决这个问题的唯一方法是让迭代器不拥有该值。所以我们修改 Nebelung 结构以保存对 Julmond 的引用:
struct Nebelung<'a: 'b, 'b> {
j: &'b Julmond<'a>,
pos: usize,
}
'a:'b 表示“'a 比'b 长”,这里是必需的。因为我们对 Julmond 的引用 j 一定不能比 Julmond 的借用内容长。好吧,我们的 Nebelung 不再是 Julmond 的主人了。只是一个借款人。现在我们可以像这样为它实现 Iterator 特性:
impl<'a, 'b> Iterator for Nebelung<'a, 'b> {
type Item = &'b [i32];
fn next(&mut self) -> Option<&'b [i32]> {
if self.pos < self.j.0.len() {
let tmp_pos = self.pos;
self.pos += 1;
Some(&self.j[tmp_pos])
} else {
None
}
}
}
不需要链接 self 和传出引用的生命周期,因为我们只是 return引用一些我们不是其所有者的值。所以对 &self.j[tmp_pos] 的调用不再是从 self 借来的。它是从 Julmond 借来的(通过索引实现)。
无论您为哪种类型实现 Iterator 特性。如果类型拥有该值,则不能有下一个(或 next_back)return 引用。让您的类型借用 SubImage。