引出问题
在学习所有权时,我从网上发现了这段代码:
fn overwrite(dest: &mut T, original: &T) {
*dest = *original;
}
这段代码是用一个共享引用的值去覆盖一个可变引用的值。这里因*操作有move值的要求,所以T必须实现copy,如果original类型简单,拷贝一次也无所谓,那如果original又大又复杂呢?这里把original的共享引用传给dest不是更高效?
身体力行
当然上面代码简单的把签名里的: Copy给去掉肯定不行,那如何实现呢?虽然我觉得我上面的想法理论上可行,但还是先验证一下:
use std::ptr;
let a = "a".to_string();
let mut b = &("b".to_string());
b = &a;
// overwrite_with_no_copy(b, &a);
assert!(ptr::eq(&a, b));
那我就信心满满的写下overwrite_with_no_copy这个版本:
use std::ptr;
fn overwrite_with_no_copy(output: &'a mut String, input: &'a String) {
output = input;
}
fn main() {
let a = "a".to_string();
let mut b = &("b".to_string());
overwrite_with_no_copy(b, &a);
assert!(ptr::eq(&a, b));
}
但这里报错:
error[E0308]: mismatched types
--> src\main.rs:3:14
|
3 | output = input;
| ^^^^^ types differ in mutability
|
= note: expected mutable reference `&'a mut std::string::String`
found reference `&'a std::string::String`
上面提示其实挺清楚了,但我当时就是参不透。所以我又群上求助,但因为我表达能力有限,大家始终不明白我的“初心”。好在群里的大佬“@沙渺”他一眼就猜中了我的意图,给我了如下版本:
fn overwrite_with_no_copy(output: &mut &'a String, input: &'a String) {
*output = input;
}
fn main() {
use std::ptr;
let b = "b".to_string();
let mut c = &("trash".to_string());
overwrite_with_no_copy(&mut c, &b);
println!("ptr(B):{:p}, ptr(*C):{:p}", &b, &*c);
assert!(ptr::eq(&*c, &b));
}
原来我离正解如此之近了,是我之前纠结在如何把&T传给&mut T,原来需要把&T传给&mut &T,再次感谢@沙渺!
这里我还从大佬的代码里学习到原来println!的格式输出还能打印内存地址,于是顺藤摸瓜,我又复习了一遍std::fmt,其中重点我拿小本本摘录如下: When requesting that an argument be formatted with a particular type, you are actually requesting that an argument ascribes to a particular trait. This allows multiple actual types to be formatted via {:x} (like i8 as well as isize). The current mapping of types to traits is:nothing ⇒ Display
? ⇒ Debug
x? ⇒ Debug with lower-case hexadecimal integers
X? ⇒ Debug with upper-case hexadecimal integers
o ⇒ Octal
x ⇒ LowerHex
X ⇒ UpperHex
p ⇒ Pointer
b ⇒ Binary
e ⇒ LowerExp
E ⇒ UpperExp
What this means is that any type of argument which implements the fmt::Binary trait can then be formatted with {:b}. Implementations are provided for these traits for a number of primitive types by the standard library as well. If no format is specified (as in {} or {:6}), then the format trait used is the Display trait.
继续深挖
人家原来是泛型,那改成了泛型试试,试试也能通过:
fn overwrite_with_no_copy(output: &mut &'a T, input: &'a T) {
*output = input;
}
后来觉得'a, T这样还是有些啰嗦,进一步简化:
fn overwrite_with_no_copy(output: &mut U, input: U) {
*output = input;
}
也能编译通过。看来适当的抽象还能隐藏掉烦人的生命周期!
事后我仍觉得这里的U应该被限制一下,忽然我灵光一现:我经常看到貌似无用的生命周期表达,会不会用处就在这里,于是我进一步改进:
fn overwrite_with_no_copy(output: &mut U, input: U) {
*output = input;
}
至此,虽然我还不能明确解释这种表达方式,但我觉得我这次get到了Rust的真谛。
总结
“回”字的写法有四种,并且一种比一种优雅深邃....