将元素添加到不可变向量 rust

Add element to immutable vector rust

我正在尝试利用函数式编程和递归在 Rust 中创建用户输入验证函数。如何 return 一个不可变向量,其中一个元素连接到末尾?

fn get_user_input(output_vec: Vec<String>) -> Vec<String> {
    // Some code that has two variables: repeat(bool) and new_element(String)
    if !repeat {
        return output_vec.add_to_end(new_element); // What function could "add_to_end" be?
    }
    get_messages_from_user(output_vec.add_to_end(new_element)) // What function could "add_to_end" be?
}

其他所有功能都有:
push 将可变向量添加到可变向量
append 将一个元素添加到可变向量的末尾
concat 将不可变向量添加到不可变向量
??? 将一个元素添加到不可变向量的末尾

我能够开始工作的唯一解决方案是使用:

[write_data, vec![new_element]].concat()

但这似乎效率低下,因为我只为一个元素创建一个新向量(因此大小在编译时已知)。

您将 Rust 与一种您只能引用对象的语言混淆了。在 Rust 中,代码可以拥有对象的独占所有权,因此你不需要对改变一个 可以 共享的对象那么小心,因为你 知道对象是否共享。

例如,这是有效的JavaScript代码:

const a = [];
a.push(1);

这是可行的,因为 a 不包含数组,它包含对数组的 引用。1 const 防止 a 被重新指向不同的对象,但它 不会 使数组本身不可变。

因此,在这些类型的语言中,纯函数式编程试图避免改变任何状态,例如将一个项目推入一个作为参数的数组中:

function add_element(arr) {
  arr.push(1); // Bad! We mutated the array we have a reference to!
}

相反,我们这样做:

function add_element(arr) {
  return [...arr, 1]; // Good! We leave the original data alone.
}

根据你的函数签名,你在 Rust 中拥有的是一个 完全不同的场景! 在你的情况下,output_vec 由函数本身拥有,并且没有程序中的其他实体可以访问它。因此,没有理由避免改变它,如果这是你的目标:

fn get_user_input(mut output_vec: Vec<String>) -> Vec<String> {
//       Add mut  ^^^

您必须记住,任何 non-reference 都是拥有的价值。 &Vec<String> 将是对其他对象拥有的向量的不可变引用,但 Vec<String> 是此代码拥有的向量,其他人无法访问。

不相信我?这是一个简单的损坏代码示例,可以证明这一点:

fn take_my_vec(y: Vec<String>) { }

fn main() {
    let mut x = Vec::<String>::new();
    
    x.push("foo".to_string());
    
    take_my_vec(x);
    
    println!("{}", x.len()); // E0382
}

表达式 x.len() 导致 compile-time 错误,因为向量 x 移动 到函数参数中,而我们没有不再拥有它。

那么为什么函数不应该改变它现在拥有的向量呢?调用方不能再使用了。


总而言之,函数式编程在 Rust 中看起来有点不同。在其他无法传达“我给你这个对象”的语言中,你必须避免改变给定的值,因为调用者可能不希望你更改它们。在 Rust 中,谁拥有一个值是明确的,参数反映了:

  • 参数是一个值(Vec<String>)吗?该函数现在拥有该值,调用者放弃了它并且不能再使用它。如果需要,请改变它。
  • 参数是否为不可变引用 (&Vec<String>)?该函数不拥有它,并且它无论如何也不能改变它,因为 Rust 不允许。您可以克隆它并对克隆体进行变异。
  • 参数是否为可变引用 (&mut Vec<String>)?调用者必须显式地为函数提供一个可变引用,因此允许函数对其进行修改——但函数仍然不拥有该值。该函数可以改变它、克隆它或两者兼而有之——这取决于该函数应该做什么。

如果您按值接受参数,那么如果您出于任何原因需要更改它,就没有理由不使用它 mut。请注意,此细节(函数参数的可变性)甚至不是函数 public 签名的一部分,因为这与调用者无关。他们把物品送人了。


请注意,对于具有类型参数的类型(如 Vec),所有权的其他表达方式也是可能的。这里有几个例子(这不是一个详尽的列表):

  • Vec<&String>:您现在拥有一个矢量,但您不拥有它包含引用的 String 个对象。
  • &Vec<&String>:您被授予 read-only 访问字符串引用向量的权限。您可以克隆此向量,但您仍然无法更改字符串,例如只能重新排列它们。
  • &Vec<&mut String>:您可以 read-only 访问 可变 字符串引用的向量。您无法重新排列字符串,但可以自行更改字符串。
  • &mut Vec<&String>:同上但相反:允许重新排列字符串引用但不能更改字符串。

1 一个很好的理解方式是 JavaScript 中的 non-primitive 值总是 Rc<RefCell<T>> 的值,所以你' 将句柄传递给具有内部可变性的对象。 const 只会使 Rc<> 不可变。