给定一个函数foo
,它接受对切片的可变引用。它可以通过 a = &mut a[1..]
来减少切片的长度。但是,我一直无法弄清楚如何编写一个以相同方式改变 a
的函数。
fn foo(mut a: &mut [i32]) {
a = &mut a[1..];
inc(&mut a);
}
fn inc<'a: 'b, 'b>(s: &'b mut &'a mut [i32]) {
*s = &mut s[1..];
}
inc
是编写这样一个函数的尝试。但是,它失败了:
Output of rustc nightly (Compiler #1)
error[E0623]: lifetime mismatch
--> <source>:3:9
|
2 | fn inc<'a: 'b, 'b>(s: &'b mut &'a mut [i32]) {
| ---------------------
| |
| these two types are declared with different lifetimes...
3 | *s = &mut s[1..];
| ^^^^^^^^^^^ ...but data from `s` flows into `s` here
最佳答案
一个更简单的错误示例是
fn oops<'a: 'b, 'b>(a: &'b mut &'a mut i32) {
let _: &'a mut i32 = *a; // error!
}
这里的问题是,拥有 a
(对某些数据的可变引用)可以让您变异(更改)它指向的值,但它不允许您移动> 值(不留下任何东西)。在 oops
中,由于一次只能对任何内容有一个可变引用,因此 *a
需要移动到 _
,但这会使 *a
没有数据,而 a
指向“垃圾”。但无论是谁将 *a
借给 oops
,都希望 *a
在归还时具有值(value),因此必须禁止这种行为。 (我说“垃圾”是因为该移动可能不会真正改变 *a
,如果我们强制执行它可能不会出错。但是如果我们不关心性能,该移动原则上可能会破坏 *a
以强制执行“一次可变借用”规则,在这种情况下你就完蛋了。)
在您的情况下, index_mut
(正在实现 [1..]
语法)采用 &mut [i32]
,因此 *s
需要移至该函数。但这使得 *s
没有值,并且您不拥有 *s
,这是非法的。你最终会收回值(value)并不重要。例如。如果 index_mut
发生 panic ,调用者将期望它借给 inc
的任何变量有一个值,但不会有任何值。相反, foo
确实拥有 a
,因此可以在 a
中的 index_mut
持续时间内将 a = &mut a[1..]
保持为无值(编译器可以跟踪自有变量何时具有值和不具有值)。
一个修复(不更改界面)是在操作时将默认值粘贴到 *s
中。 impl<'a, T> Default for &'a mut[T]
提供了这个(默认值指向一个空切片),并且 std::mem::take
以安全的方式封装了 Default
实现。
fn inc<'a: 'b, 'b>(s: &'b mut &'a mut [i32]) {
*s = &mut std::mem::take(s)[1..];
}
我同意@cdhowie的观点
fn inc<'a>(s: &'a mut [i32]) -> &'a mut [i32] {
return s[1..];
}
并且要求 a = inc(a)
使用比 inc(&mut a)
接口(interface)更好。
关于rust - 封装可变切片修改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71551690/