我对Rust还是陌生的,正在尝试学习使用借位检查器的惯用方式。
我正在尝试编写一个简单的函数,该函数接受一个slice(对数据类型通用)并返回最后一个元素,以使我以后可以对该slice进行更改。天真的实现方式给我一个错误:
fn last_element<T>(list: &[T]) -> T {
list[list.len() - 1]
}
fn main() {
let mut slice = [1, 2, 3, 4, 5];
let x = last_element(&slice);
println!("{}", x);
// I want to be able to mutate slice after extracting last element
slice[2] = 17;
}
错误是cannot move out of type
[T] , a non-copy slice
。我看到一种解决方法是让函数返回引用:
fn last_element<T>(list: &[T]) -> &T {
&list[list.len() - 1]
}
fn main() {
let mut slice = [1, 2, 3, 4, 5];
let x = last_element(&slice);
println!("{}", x);
// I want to be able to mutate slice after extracting last element
slice[2] = 17;
}
但是随后我得到slice[2] = 17
的错误,因为在分配x
时借用了切片。我确实希望能够在致电last_element
之后进行变异。我发现的一种解决方法是取消引用x
,我认为这会消耗借用:
fn last_element<T>(list: &[T]) -> &T {
&list[list.len() - 1]
}
fn main() {
let mut slice = [1, 2, 3, 4, 5];
let x = *last_element(&slice);
println!("{}", x);
// I want to be able to mutate slice after extracting last element
slice[2] = 17;
}
这是否是最惯用的方法,可以实现获得切片的最后一个元素并随后仍然使切片变异的目的?或者如果我编写好的代码,我以后甚至应该永远不要做这种变异吗?
答案 0 :(得分:4)
如果使用简单类型,例如i32
和其他固定大小的小型结构,则这些类型通常实现Copy
。此特征是一个标记,它告诉编译器在可能会移动值的情况下将其复制到内存中。实际上,这是错误消息在引用non-copy slice
时的含义:如果元素未实现Copy
,则必须移动它们,这将使切片处于无效状态,这是不允许的。
如果您将函数限制为仅期望Copy
类型,那么它将很乐意为您复制这些值:
fn last_element<T: Copy>(list: &[T]) -> T {
list[list.len() - 1]
}
如果元素的类型可能更复杂且未实现Copy
,则可以将函数约束更广泛,以实现任何实现Clone
的类型,然后调用{{1 }}在元素返回之前:
clone()
与复制相比,克隆操作通常要重得多,因为克隆操作通常是在Rust代码的每个字段中实现的,而fn last_element<T: Clone>(list: &[T]) -> T {
list[list.len() - 1].clone()
}
是对原始内存进行的较低级别的操作。
最后一个选择是只返回对最后一个元素的引用。然后,函数的调用者可以决定如何处理它。如果它是Copy
类型(如数字),则取消引用它会复制它:
Copy
如果元素是fn last_element<T>(list: &[T]) -> &T {
&list[list.len() - 1]
}
fn main() {
let mut slice = [1, 2, 3, 4, 5];
let x = *last_element(&slice);
}
而不是Clone
,那么您可以在此时显式Copy
,而不是取消引用:
clone