Ale*_*lex 3 recursion functional-programming vector immutability rust
我正在尝试利用函数式编程和递归在 Rust 中创建一个用户输入验证函数。如何返回一个不可变向量,其中一个元素连接到末尾?
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_user_input(output_vec.add_to_end(new_element)) // What function could "add_to_end" be?
}
Run Code Online (Sandbox Code Playgroud)
还有其他所有功能的函数:
push
将可变向量添加到可变
append
向量 将元素添加到可变向量的末尾
concat
将不可变向量添加到不可变向量
???
将元素添加到不可变向量的末尾
我能够做到的唯一解决方案开始工作正在使用:
[write_data, vec![new_element]].concat()
Run Code Online (Sandbox Code Playgroud)
但这似乎效率低下,因为我只为一个元素创建一个新向量(因此大小在编译时已知)。
cdh*_*wie 19
您将 Rust 与一种只引用对象的语言混淆了。在 Rust 中,代码可以拥有对象的独占所有权,因此您无需小心地修改可以共享的对象,因为您知道该对象是否是共享的。
例如,这是有效的 JavaScript 代码:
const a = [];
a.push(1);
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为a
不包含数组,它包含对数组的引用。1 防止const
被a
重新指向不同的对象,但它不会使数组本身不可变。
因此,在这些类型的语言中,纯函数式编程试图避免改变任何状态,例如将一个项目推送到作为参数的数组上:
function add_element(arr) {
arr.push(1); // Bad! We mutated the array we have a reference to!
}
Run Code Online (Sandbox Code Playgroud)
相反,我们做这样的事情:
function add_element(arr) {
return [...arr, 1]; // Good! We leave the original data alone.
}
Run Code Online (Sandbox Code Playgroud)
考虑到你的函数签名,你在 Rust 中所拥有的,是一个完全不同的场景! 在您的情况下,output_vec
由函数本身拥有,程序中的任何其他实体都无法访问它。因此,如果这是您的目标,则没有理由避免对其进行突变:
fn get_user_input(mut output_vec: Vec<String>) -> Vec<String> {
// Add mut ^^^
Run Code Online (Sandbox Code Playgroud)
您必须记住,任何非引用都是拥有的价值。&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
}
Run Code Online (Sandbox Code Playgroud)
该表达式x.len()
会导致编译时错误,因为向量x
已移至函数参数中,而我们不再拥有它。
那么为什么该函数不应该改变它现在拥有的向量呢?调用者不能再使用它。
总而言之,函数式编程在 Rust 中看起来有点不同。在其他无法传达“我正在给你这个对象”的语言中,你必须避免改变给定的值,因为调用者可能不希望你更改它们。在 Rust 中,谁拥有一个值是明确的,并且这个参数反映了:
Vec<String>
) 吗?该函数现在拥有该值,调用者将其放弃并且不能再使用它。如果需要的话,改变它。&Vec<String>
) 吗?该函数不拥有它,并且无论如何也不能改变它,因为 Rust 不允许这样做。你可以克隆它并变异克隆。&mut Vec<String>
) 吗?调用者必须显式地为函数提供可变引用,因此授予函数更改它的权限——但该函数仍然不拥有该值。该函数可以改变它、克隆它,或者两者兼而有之——这取决于该函数应该做什么。如果您按值获取参数,那么无论mut
出于何种原因需要更改它,都没有理由不这样做。请注意,这个细节(函数参数的可变性)甚至不是函数公共签名的一部分,因为它不是调用者的事。他们把这个东西送人了。
请注意,对于具有类型参数(如Vec
)的类型,其他所有权表达式也是可能的。以下是一些示例(这不是详尽的列表):
Vec<&String>
:您现在拥有一个向量,但您不拥有String
它包含引用的对象。&Vec<&String>
:您被授予对字符串引用向量的只读访问权限。例如,您可以克隆此向量,但仍然无法更改字符串,只能重新排列它们。&Vec<&mut String>
:您被授予对可变字符串引用向量的只读访问权限。您无法重新排列字符串,但可以更改字符串本身。&mut Vec<&String>
:与上面类似但相反:您可以重新排列字符串引用,但不能更改字符串。1一个好的思考方法是 JavaScript 中的非原始值始终是 的值Rc<RefCell<T>>
,因此您将传递具有内部可变性的对象的句柄。const
只会让事情Rc<>
变得不可变。