【问题标题】:How to copy last element from slice如何从切片中复制最后一个元素
【发布时间】:2019-02-02 06:39:33
【问题描述】:

我是 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;
}

错误是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;
}

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

【问题讨论】:

    标签: rust copy slice


    【解决方案1】:

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

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

    fn last_element<T: Copy>(list: &[T]) -> T {
        list[list.len() - 1]
    }
    

    如果你的元素的类型可能更复杂,并且没有实现Copy,那么你可以将函数限制在更广泛的范围内,任何实现Clone的类型,然后在之前的元素上调用clone()返回:

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

    克隆通常是比复制更繁重的操作,因为它通常在 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);
    }
    

    如果元素是 Clone 而不是 Copy,那么您可以在此时显式地 clone 代替取消引用:

    let x = last_element(&slice).clone();
    

    【讨论】:

    • 感谢您的回复!这是否意味着将复制整个切片? (如果是这样,有没有办法只复制最后一个元素,以防切片很长?)
    • @rampatowl 不,整个切片只是通过引用传递。只是您按值返回元素。所以这将确保元素将被复制而不是移动。
    猜你喜欢
    • 1970-01-01
    • 2014-11-28
    • 2014-05-21
    • 1970-01-01
    • 2014-04-27
    • 2021-12-16
    • 1970-01-01
    • 2019-03-27
    • 2018-08-03
    相关资源
    最近更新 更多