函数参数如何允许改变变量但不能指向任何其他变量?
How can a function argument allow mutating a variable but not be capable of pointing to any other variable?
我想要一个函数来调用其他相互递归的函数,但我已经有了这样的类型签名:
fn f1(mut index: &mut usize, ..)
fn f2(mut index: &mut usize, ..)
我真的想要一组相互递归的函数,它们只能改变我在 main()
函数中定义的 index
变量,不能 指向任何其他变量。
我已经阅读了 的 2 个答案,并且我尝试了几种方法,但仍然无法实现我想要的。我觉得我不太理解这些概念。
以下是我目前理解的证据:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the referred value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
// doesn't work because of lifetimes
// this changes where is pointing a (Copy?) reference of n
// passed value does not change
/*
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
*/
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_ = {}", index);
}
如果我误解了我的代码,我将不胜感激。
我开始认为我想要的已经完成了:
index
必须引用其更新值 => mut index
index
必须能够更改引用值并将可变引用传递给其他函数。 => &mut usize
- 如果它是另一个具有相同类型
(mut index2: &mut usize)
的函数参数,编译器不会让我有 2 个指向相同内存位置的可变引用。
引用是指向其他对象的变量。引用不会折叠,所以在 Rust 中你可以有一个引用的引用。
您的链接问题描述了 x: &mut T
和 mut x: T
之间的区别。 mut x: &T
表示您可以更改引用指向的位置,但不能更改它指向的值。
传递给函数时将复制非引用值(或移动,如果未实现 Copy
)。
一个工作示例:
// with &mut I can change the referred value of n
fn mutate_usize_again(n: &mut usize) {
*n += 1;
*n += 70;
}
fn mutate_usize_two_times(n: &mut usize) {
*n = 8;
// just pass it along
mutate_usize_again(n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the **local** value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_one_time_referred_value = {}", index);
}
考虑到你的标题,你对这个原则感到困惑。
简明扼要的答案:
You shall always follow the even rule. You have same amount of slashes and same amount of dereferences. Together they give an even number.
啊!旧 pointer/pointee 问题。别担心,这是一个常见的绊脚石。坚持下去,在某个时候它会发出咔嗒声,然后它就会变得显而易见。
什么是引用?
引用是间接的。存在于别处的事物的地址。
打个比方:
- 一个函数框架是一个壁橱,有很多抽屉,
- 每个抽屉可能包含一个值。
暂时,让我们忘记 types、stack 和 heap:我们只有壁橱和抽屉:
- 一个值在一个抽屉里,它可能被移动或复制到另一个抽屉,
- a reference是这个值的地址,表示为一对ID(壁橱ID,抽屉ID)。
注意:只提抽屉ID是无意义的,所有壁橱都有抽屉0...
如何使用抽屉?
让我们想象一个(无类型)简单的例子,加法函数:
fn add(left, right) { left + right }
现在,当我们调用 add(3, 4)
时会发生什么?
Function
call
+-add-+ <-- New closet
| 3 | <-- Drawer 0: value 3
+-----+
| 4 | <-- Drawer 1: value 4
+-----+
注意:在下文中,我将壁橱表示为数组。这个壁橱是 [3, 4]
。我也会 "number" 壁橱使用字母,以避免混淆壁橱和抽屉,所以这个壁橱可以是 d
,而 d
的第一个抽屉将是 0@d
(其中包含 3 个)。
函数定义为"take the value in drawer 0, take the value in drawer 1, add them together in drawer 2, return content of drawer 2"。
让我们来点有趣的东西;并引入参考文献:
fn modify() {
let x = 42;
let y = &x;
*y = 32;
}
modify
是做什么的?
- 致电
modify
=> 给了我们一个新衣橱 a: []
,
let x = 42;
=> 我们的衣柜现在是 a: [42]
,
let y = &x;
=> 我们的衣柜现在是 a: [42, 0@a]
,
现在是症结所在:*y = 32;
。这意味着将 32 放入 y
指向的抽屉中。因此壁橱现在是:a: [32, 0@a]
.
你的第一个例子
让我们看一下你的第一个例子,在 main
的情况下:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn main() {
let mut x = 24;
mutate_usize_gain(&mut x);
}
那么,这是怎么回事?
- 致电
main
=> 分配了一个新衣橱 a: []
,
let mut x = 24;
=> a: [24]
,
- 呼叫
mutate_usize_again
=> 分配了一个新衣橱 b: []
,
- 与
&mut x
=> b: [0@a]
,
*n += 1;
=> 将n
指向的抽屉加1(0@a),这意味着b
不会改变,但是a
会改变!现在 a: [25]
.
n += 70
=> 将 70 添加到引用中...这没有意义,引用没有算术运算。
你的第二个例子
让我们继续你的第二个例子,扩充:
// Parameter renamed because it's confusing to always use the same letter
fn mutate_usize_again(z: &mut usize) { *z += 1; }
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn main() {
let mut x = 24;
mutate_usize_two_times(&mut x);
}
我们需要 3 个壁橱!
- 致电
main
=> 分配了一个新衣橱 a: []
,
let mut x = 24
=> a: [24]
,
- 致电
mutate_usize_two_times
=> 分配了一个新衣橱 b: []
,
- 与
n: &mut usize = &mut x
=> b: [0@a]
,
*n = 8
=> 将 8 存储在 n
指向的抽屉中:a: [8]
,b
不变,
- 致电
mutate_usize_again
=> 分配了一个新衣橱 c: []
,
- 与
z: &mut usize = &mut n
=> 错误。 &mut n
的类型是 &mut &mut usize
不能赋值给 z
, 类型的参数
- 假设您使用了
z: &mut usize = n
=> c: [0@a]
,
*z += 1
=> z
指向的抽屉中的值加1:a: [9]
、b
和c
不变。
你可以引用一个引用,但这不是你想在这里做的。我们确实可以通过定义来扭曲示例以使其工作:
fn mutate_usize_again(z: &mut &mut usize) { **z += 1; }
^ ^~~ dereference TWICE
|~~~~~~~~~~~~~~ two levels of reference
在这种情况下,从对mutate_usize_again
的调用重新开始:
- 调用之前,我们有:
a: [8]
、b: [0@a]
- 致电
mutate_usize_again
=> 分配了一个新衣橱 c: []
,
- with
z: &mut &mut usize = &mut n
=> c: [0@b]
<- 对引用的引用!
**z += 1
=> 将 z
指向的引用指向的抽屉中的值加 1。 **z += 1
是 **0@b += 1
即 *0@a += 1
,它将 a
修改为 a: [9]
并保留 b
和 c
不变。
你的第三个例子
你的第三个例子,增强版:
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time_mutable_pointer(x);
}
我们走吧:
- 致电
main
=> 分配了一个新衣橱 a: []
,
let x = 42;
=> a: [42]
,
- 呼叫
mutate_usize_one_time_mutable_pointer
=> 分配了一个新衣橱 b: []
- 与
n: mut usize = x
=> b: [42]
,
n = 48
=>修改n
的值:b: [48]
,注意a
不变
mutate_usize_one_time_mutable_pointer
结束,丢弃 b
,a: [42]
。
这里没有涉及指针,函数名是误导。
你最后的例子
你的第四个例子,扩充:
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time(&x);
}
我们走吧:
- 致电
main
=> 分配了一个新衣橱 a: []
,
let x = 42;
=> a: [42]
,
- 致电
mutate_usize_one_time
=> 分配了一个新衣橱 b: []
,
n: &size = &x
=> b: [0@a]
,
48
=> b: [0@a, 48]
&48
=> b: [0@a, 48, 1@b]
n = &48
=> 会导致 b: [1@b, 48]
但是生命周期禁止引用指向 过去 的东西,这是被禁止的。
希望它开始有意义。如果没有……那就去 运行,和朋友出去,清醒一下,读另一个解释,再清醒一下,然后再回来。它会一点一点渗入 ;)
我想要一个函数来调用其他相互递归的函数,但我已经有了这样的类型签名:
fn f1(mut index: &mut usize, ..)
fn f2(mut index: &mut usize, ..)
我真的想要一组相互递归的函数,它们只能改变我在 main()
函数中定义的 index
变量,不能 指向任何其他变量。
我已经阅读了
以下是我目前理解的证据:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the referred value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
// doesn't work because of lifetimes
// this changes where is pointing a (Copy?) reference of n
// passed value does not change
/*
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
*/
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_ = {}", index);
}
如果我误解了我的代码,我将不胜感激。
我开始认为我想要的已经完成了:
index
必须引用其更新值 =>mut index
index
必须能够更改引用值并将可变引用传递给其他函数。 =>&mut usize
- 如果它是另一个具有相同类型
(mut index2: &mut usize)
的函数参数,编译器不会让我有 2 个指向相同内存位置的可变引用。
引用是指向其他对象的变量。引用不会折叠,所以在 Rust 中你可以有一个引用的引用。
您的链接问题描述了 x: &mut T
和 mut x: T
之间的区别。 mut x: &T
表示您可以更改引用指向的位置,但不能更改它指向的值。
传递给函数时将复制非引用值(或移动,如果未实现 Copy
)。
一个工作示例:
// with &mut I can change the referred value of n
fn mutate_usize_again(n: &mut usize) {
*n += 1;
*n += 70;
}
fn mutate_usize_two_times(n: &mut usize) {
*n = 8;
// just pass it along
mutate_usize_again(n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the **local** value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_one_time_referred_value = {}", index);
}
考虑到你的标题,你对这个原则感到困惑。 简明扼要的答案:
You shall always follow the even rule. You have same amount of slashes and same amount of dereferences. Together they give an even number.
啊!旧 pointer/pointee 问题。别担心,这是一个常见的绊脚石。坚持下去,在某个时候它会发出咔嗒声,然后它就会变得显而易见。
什么是引用?
引用是间接的。存在于别处的事物的地址。
打个比方:
- 一个函数框架是一个壁橱,有很多抽屉,
- 每个抽屉可能包含一个值。
暂时,让我们忘记 types、stack 和 heap:我们只有壁橱和抽屉:
- 一个值在一个抽屉里,它可能被移动或复制到另一个抽屉,
- a reference是这个值的地址,表示为一对ID(壁橱ID,抽屉ID)。
注意:只提抽屉ID是无意义的,所有壁橱都有抽屉0...
如何使用抽屉?
让我们想象一个(无类型)简单的例子,加法函数:
fn add(left, right) { left + right }
现在,当我们调用 add(3, 4)
时会发生什么?
Function
call
+-add-+ <-- New closet
| 3 | <-- Drawer 0: value 3
+-----+
| 4 | <-- Drawer 1: value 4
+-----+
注意:在下文中,我将壁橱表示为数组。这个壁橱是 [3, 4]
。我也会 "number" 壁橱使用字母,以避免混淆壁橱和抽屉,所以这个壁橱可以是 d
,而 d
的第一个抽屉将是 0@d
(其中包含 3 个)。
函数定义为"take the value in drawer 0, take the value in drawer 1, add them together in drawer 2, return content of drawer 2"。
让我们来点有趣的东西;并引入参考文献:
fn modify() {
let x = 42;
let y = &x;
*y = 32;
}
modify
是做什么的?
- 致电
modify
=> 给了我们一个新衣橱a: []
, let x = 42;
=> 我们的衣柜现在是a: [42]
,let y = &x;
=> 我们的衣柜现在是a: [42, 0@a]
,
现在是症结所在:*y = 32;
。这意味着将 32 放入 y
指向的抽屉中。因此壁橱现在是:a: [32, 0@a]
.
你的第一个例子
让我们看一下你的第一个例子,在 main
的情况下:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn main() {
let mut x = 24;
mutate_usize_gain(&mut x);
}
那么,这是怎么回事?
- 致电
main
=> 分配了一个新衣橱a: []
, let mut x = 24;
=>a: [24]
,- 呼叫
mutate_usize_again
=> 分配了一个新衣橱b: []
, - 与
&mut x
=>b: [0@a]
, *n += 1;
=> 将n
指向的抽屉加1(0@a),这意味着b
不会改变,但是a
会改变!现在a: [25]
.n += 70
=> 将 70 添加到引用中...这没有意义,引用没有算术运算。
你的第二个例子
让我们继续你的第二个例子,扩充:
// Parameter renamed because it's confusing to always use the same letter
fn mutate_usize_again(z: &mut usize) { *z += 1; }
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn main() {
let mut x = 24;
mutate_usize_two_times(&mut x);
}
我们需要 3 个壁橱!
- 致电
main
=> 分配了一个新衣橱a: []
, let mut x = 24
=>a: [24]
,- 致电
mutate_usize_two_times
=> 分配了一个新衣橱b: []
, - 与
n: &mut usize = &mut x
=>b: [0@a]
, *n = 8
=> 将 8 存储在n
指向的抽屉中:a: [8]
,b
不变,- 致电
mutate_usize_again
=> 分配了一个新衣橱c: []
, - 与
z: &mut usize = &mut n
=> 错误。&mut n
的类型是&mut &mut usize
不能赋值给z
, 类型的参数
- 假设您使用了
z: &mut usize = n
=>c: [0@a]
, *z += 1
=>z
指向的抽屉中的值加1:a: [9]
、b
和c
不变。
你可以引用一个引用,但这不是你想在这里做的。我们确实可以通过定义来扭曲示例以使其工作:
fn mutate_usize_again(z: &mut &mut usize) { **z += 1; }
^ ^~~ dereference TWICE
|~~~~~~~~~~~~~~ two levels of reference
在这种情况下,从对mutate_usize_again
的调用重新开始:
- 调用之前,我们有:
a: [8]
、b: [0@a]
- 致电
mutate_usize_again
=> 分配了一个新衣橱c: []
, - with
z: &mut &mut usize = &mut n
=>c: [0@b]
<- 对引用的引用! **z += 1
=> 将z
指向的引用指向的抽屉中的值加 1。**z += 1
是**0@b += 1
即*0@a += 1
,它将a
修改为a: [9]
并保留b
和c
不变。
你的第三个例子
你的第三个例子,增强版:
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time_mutable_pointer(x);
}
我们走吧:
- 致电
main
=> 分配了一个新衣橱a: []
, let x = 42;
=>a: [42]
,- 呼叫
mutate_usize_one_time_mutable_pointer
=> 分配了一个新衣橱b: []
- 与
n: mut usize = x
=>b: [42]
, n = 48
=>修改n
的值:b: [48]
,注意a
不变mutate_usize_one_time_mutable_pointer
结束,丢弃b
,a: [42]
。
这里没有涉及指针,函数名是误导。
你最后的例子
你的第四个例子,扩充:
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time(&x);
}
我们走吧:
- 致电
main
=> 分配了一个新衣橱a: []
, let x = 42;
=>a: [42]
,- 致电
mutate_usize_one_time
=> 分配了一个新衣橱b: []
, n: &size = &x
=>b: [0@a]
,48
=>b: [0@a, 48]
&48
=>b: [0@a, 48, 1@b]
n = &48
=> 会导致b: [1@b, 48]
但是生命周期禁止引用指向 过去 的东西,这是被禁止的。
希望它开始有意义。如果没有……那就去 运行,和朋友出去,清醒一下,读另一个解释,再清醒一下,然后再回来。它会一点一点渗入 ;)