MST

星途 面试题库

面试题:Rust结构体移动语义与函数调用

编写一个Rust函数,该函数接收一个结构体作为参数,函数内部对结构体进行一些操作,然后返回这个结构体。解释在函数调用前后结构体的移动语义是如何工作的,并说明如果要避免移动,有哪些方法可以实现。```ruststruct Data { value: Vec<i32>}// 在这里编写函数```
39.4万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试
struct Data {
    value: Vec<i32>
}

fn process_data(mut data: Data) -> Data {
    // 对结构体进行一些操作,例如修改value
    data.value.push(42);
    data
}

移动语义解释

  1. 函数调用前
    • 当定义 Data 结构体实例时,例如 let my_data = Data { value: vec![1, 2, 3] };my_data 拥有 value 这个 Vec<i32> 的所有权。
    • Vec<i32> 是一个堆分配类型,它在栈上有一个胖指针(包含指向堆内存的指针、长度和容量)。
  2. 函数调用时
    • 当调用 process_data(my_data) 时,my_data 的所有权被移动到 process_data 函数的参数 data 中。这意味着 my_data 不再有效,不能再被使用。在 Rust 中,移动操作通常是廉价的,因为它只是转移了所有权,而不是复制数据。
  3. 函数返回后
    • 函数 process_data 返回修改后的 data。调用者接收返回值并获得其所有权。例如 let new_data = process_data(my_data);new_data 现在拥有修改后的 Vec<i32> 的所有权。

避免移动的方法

  1. 使用引用
    • 可以将函数参数改为引用,这样所有权不会被移动。
    struct Data {
        value: Vec<i32>
    }
    
    fn process_data_ref(data: &mut Data) {
        data.value.push(42);
    }
    
    • 调用方式:
    let mut my_data = Data { value: vec![1, 2, 3] };
    process_data_ref(&mut my_data);
    
    • 这里 process_data_ref 函数接收 Data 结构体的可变引用,函数内部可以修改 Data 的内容,但所有权仍然在调用者手中。
  2. 克隆数据
    • 如果 Data 结构体实现了 Clone 特质,可以在函数调用前克隆数据,这样原始数据的所有权不变。
    struct Data {
        value: Vec<i32>
    }
    impl Clone for Data {
        fn clone(&self) -> Data {
            Data {
                value: self.value.clone()
            }
        }
    }
    
    fn process_data_clone(data: Data) -> Data {
        data.value.push(42);
        data
    }
    
    • 调用方式:
    let my_data = Data { value: vec![1, 2, 3] };
    let new_data = process_data_clone(my_data.clone());
    
    • 这种方式会产生额外的开销,因为 clone 方法会复制 Data 结构体及其内部的 Vec<i32> 数据。