Rust随笔——结构体打印和所有权转移
结构体打印
如果想打印结构体,并不能使用如以下方式进行打印
println!("{}",rectangle);
会出现上图所示的错误,通过阅读不难得出——报错原因为Rect类型没有实现std::fmt::Display
这个trait。
第一个note建议我们使用{:?}或{:#?}来代替{}进行输出,于是尝试修改后进行构建
修改后,发现仍然报错,但报错内容发生改变。此时提示Rect不能被{:?}进行格式化。
当我们使用println!宏进行输出时,如果使用{},那么即宣告使用std::fmt::Display
。根据help的内容,提示Rect没有实现Debug这个trait.所以要尝试解决这个问题。
第一个note提示可以加入#[derive(Debug)]
给Rect或者手动为Rect实现Debug来解决,那么毫无疑问肯定采用前者处理。
此时代码就变成了下图所示
成功运行。
所有权转移
#[derive(Debug)]
struct Rect {
name: String,
width: u32,
length: u32,
}
fn main() {
let rectangle = Rect {
name: String::from("123"),
width: 50,
length: 20,
};
let rectangle2 = Rect {
width: 50,
..rectangle
};
println!("{}", rectangle.name)
}
上述代码无法正常运行,报错信息如下:
其中报错的部分rectangle.name
提示为——当所有权转移后值被借用,而值的所有权移动发生在rectangle2中。
在Rust中,除了基本类型外,其他类型的数据会被存放在堆上,在赋值时并不会在堆上拷贝一份。因为这对性能存在较大影响。
为了保证程序的安全,Rust在尝试拷贝被分配的内存时,会使第一个变量的所有权转移给第二个变量,使得第一个变量无效。在上述代码中的表现为,rectangle2中的name字段获得了rectangle的所有权,此时rectangle中的非基本类型就无法再使用了。当rectangle2离开自己的作用域时,也会自动释放自己的内存。
根据第一条note的信息,发生所有权转移的原因是String类型并没有实现Copy这个trait。
那么解决问题的方式就很清晰了,要么为String实现copy,要么使用获得所有权的rectangle2即可。
#[derive(Debug)]
struct Rect {
name: String,
width: u32,
length: u32,
}
fn main() {
let rectangle = Rect {
name: String::from("123"),
width: 50,
length: 20,
};
let rectangle2 = Rect {
name: rectangle.name.clone(),
..rectangle
};
println!("{}", rectangle.name);
println!("{}", rectangle2.width);
println!("{}", rectangle2.length);
}
既然问题出在String这个非基本类型的所有权转移,那么可不可以想办法使得其在堆上拷贝一份。答案是可行的。为堆上的数据使用clone函数,就可以实现这一点。
而另外一个解决方案就是使用rectangle2输出即可,此方法较为简单,不再赘述。