Rust随笔——结构体打印和所有权转移

发布时间 2023-07-29 23:49:15作者: Appletree24

结构体打印

如果想打印结构体,并不能使用如以下方式进行打印

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输出即可,此方法较为简单,不再赘述。