Rust中不可变变量放入Box后可修改?所有权转移相关疑问
问题解析:不可变变量赋值后可修改的原因
先看你提供的代码:
let mut a = Box::new("123".to_string()); let b = Box::new(&mut a); b.push('4'); assert_eq!("1234", b.as_str()); // lets see the types: // let x001: Box<&mut Box<String>> = b; // let x001:&mut Box<String> = *b; // let x001:Box<String> = **b; // let x001:String = ***b; // let x001:str = ****b; let c = Box::new("456".to_string()); **b = c; b.push('9'); (*b).push('9'); (**b).push('9'); (***b).push('9'); // (****b).push('9'); // no method named `push` found for type `str` in the current scope // c.push( 'a'); // cannot mutate immutable variable `c` assert_eq!("4569999", b.as_str()); ***b = "abcd".to_string(); b.push('9'); assert_eq!("abcd9", b.as_str());
你疑惑的点是:不可变的c赋值给**b后,却能通过b修改内容,是不是所有权转移导致的?会不会所有不可变变量都能这么改?
核心解释:绑定的可变性 ≠ 值的可变性
Rust里的变量可变性是绑定的属性,不是值本身的属性:
let c = Box::new("456".to_string());是不可变绑定,意味着你不能修改c这个变量本身——比如不能给c重新赋值,也不能通过c获取内部String的可变引用(所以直接调用c.push('a')会报错)。但c持有的Box<String>里的String本身是可变类型,只是被不可变绑定限制了访问方式。**b = c;这一步确实发生了所有权转移:b的类型是Box<&mut Box<String>>,解引用两次后**b是Box<String>类型,这里的赋值是把c绑定的Box<String>的所有权直接转移给**b指向的位置(也就是原来a的内存位置)。- 转移完成后,
c就不再拥有这个Box<String>的所有权了——这才是c.push('a')报错的真正原因(不是单纯因为不可变,而是c已经变成无效的空绑定了)。
为什么能通过
b修改内容?b内部持有一个&mut Box<String>类型的可变引用,通过解引用链(b→*b→**b→***b)可以最终获取到String的可变引用,自然就能调用push修改内容。- 当
c的所有权转移到**b后,这个Box<String>就处于b的可变引用链覆盖范围内,所以可以被修改。
结论:不是所有不可变变量都能被修改
这不是漏洞,而是Rust所有权和可变性规则的正常表现:
- 不可变绑定只是限制了通过该绑定对值的修改,一旦值的所有权转移到其他位置,只要新位置有可变访问权限,就可以修改值。
- 如果
c的所有权没有被转移(比如c是Copy类型,赋值时只会复制而不是转移),那c依然持有原有的值,你还是不能通过c修改它,但可以修改转移后的副本(如果副本有可变权限的话)。
内容的提问来源于stack exchange,提问作者Frank Schwidom
相关产品推荐
相关产品推荐

