如何从切片复制最后一个元素

ram*_*owl 1 copy slice rust

我是 Rust 新手,正在尝试学习使用借用检查器的惯用方式。

我正在尝试编写一个简单的函数,它接受一个切片(数据类型的通用)并返回最后一个元素,这样我就可以在之后改变切片。天真的实现给了我一个错误:

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;
}
Run Code Online (Sandbox Code Playgroud)

错误是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;
}
Run Code Online (Sandbox Code Playgroud)

但后来我收到一个错误,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;
}
Run Code Online (Sandbox Code Playgroud)

这是实现能够获取切片的最后一个元素并随后仍然改变切片的目标的最惯用的方法吗?或者,如果我编写了好的代码,我什至不应该在之后进行突变?

Pet*_*all 5

如果您正在使用简单类型,例如i32其他小型、固定大小的结构,这些类型通常实现Copy. 这个特征是一个标记,它告诉编译器在内存中复制值,否则它们会被移动。事实上,这就是错误消息在引用 a 时的含义non-copy slice:如果元素未实现Copy,则必须移动它们,这将使切片处于无效状态,这是不允许的。

如果您将函数限制为仅期望Copy类型,那么它会很乐意为您复制这些值:

fn last_element<T: Copy>(list: &[T]) -> T {
    list[list.len() - 1]
}
Run Code Online (Sandbox Code Playgroud)

如果元素的类型可能更复杂,并且不实现Copy,那么您可以将函数限制得更宽,为任何实现 的类型Clone,然后clone()在返回之前调用该元素:

fn last_element<T: Clone>(list: &[T]) -> T {
    list[list.len() - 1].clone()
}
Run Code Online (Sandbox Code Playgroud)

克隆通常是比复制更繁重的操作,因为它通常是在 Rust 代码中基于每个字段实现的,而它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);
}
Run Code Online (Sandbox Code Playgroud)

如果元素是Clone但当时不是Copy,您可以在clone此时显式地显示它,而不是取消引用:

let x = last_element(&slice).clone();
Run Code Online (Sandbox Code Playgroud)