10_rust的结构体struct

发布时间 2023-10-18 11:31:40作者: UFOFCZ

rust的struct

定义和实例化struct

使用struct关键字,并对整个struct命名。
在花括号内,对所有字段(Field)定义名称和类型。
创建struct实例:为每个字段指定具体值,无需按声明顺序进行指定。

struct User {
    name: String,
    id: u64,
    is_act: bool,
}
fn main() {
    let u1 = User {
      name: String::from("test"),
      is_act: true,
      id: 12,
    }
}

操作字段

使用点标记法操作字段,一旦struct实例是可变的,那么实例中所有字段都是可变的

struct User {
    name: String,
    id: u64,
    is_act: bool,
}
fn main() {
    let mut u1 = User {
      name: String::from("test"),
      is_act: true,
      id: 12,
    }
    u1.name = String::from("t2");
}

struct作为函数的返回值

struct User {
    name: String,
    id: u64,
    is_act: bool,
}

fn return_struct(nm: String, is_act: bool) -> User {
  User {
    name: nm,
    is_act: is_act,
    id: 0,
  }
}

当字段名与字段值对应变量名相同时,可使用字段初始化简写的方式。

struct User {
    name: String,
    id: u64,
    is_act: bool,
}

fn return_struct(name: String, is_act: bool) -> User {
  User {
    name,
    is_act,
    id: 0,
  }
}

struct的更新语法

当基于某个struct实例创建一个新实例时,可用更新语法:

fn main() {
    let mut u1 = User {
      name: String::from("test"),
      is_act: true,
      id: 12,
    }
    u1.name = String::from("t2");
    let u2 = User {
      name: String::from("test2"),
      ..u1 // 更新语法,表示剩余字段用u1的值来填充
    }
}

tuple struct

tuple struct:定义类似于tuple的struct,tuple struct整体有个命名,但里边元素没有名称。
适应:想给整个tuple起名,使其不同于其他tuple,且无需给每个元素命名。
定义tuple struct:使用struct关键字 名字(元素类型列表)

struct Color(i32, i32, i32);
struct P(i32, i32, i32);
let b = Color(0, 0, 0);
let s = P(0, 0, 0); // s和b是两个不同类型数据

可用模式匹配和点索引的方式访问字段。

Unit-Like struct(没有任何字段)

可定义没有任何字段的struct,叫Unit-Like struct(因为与(),单元类型类似).
适于需要在某个类型上实现某个trait,但没有需要存储的数据。

struct数据的所有权

struct User {
    name: String,
    id: u64,
    is_act: bool,
}

这里字段使用了String而不是&str,该struct实例拥有其所有的数据,只要struct实例有效,其字段也有效。
struct里也可放引用,需使用生命周期的知识。


看个实践:

struct Rect {
    w: u32,
    l: u32,
}

fn main() {
    let rect = (30, 40);
    println!("{}", get_area(rect));
    let rect_struct = Rect {
        w: 30,
        l: 40,
    }
    // 传入不可变的引用,传入后为不可变借用,不会改变变量的所有权,所有权依然归属于main函数
    println!("{}", get_area_struct(&rect_struct));
}

fn get_area(rect: (u32, u32)) -> u32 { // 使用tuple实现
    rect.0 * rect.1 // 无法区分谁是长和宽
}

fn get_area_struct(rect: &Rect) -> u32 { // 使用结构体的不可变的借用即可,无需获得所有权
    rect.w * rect.l
}

rust的打印与调试信息

首先看下直接打印

struct Rect {
    w: u32,
    l: u32,
}

fn main() {
    let rect = Rect {
        w: 30,
        l: 40,
    };
    // 传入不可变的引用,传入后为不可变借用,不会改变变量的所有权,所有权依然归属于main函数
    println!("{}", get_area_struct(&rect));
    println!("{}", rect);
/* 直接打印,编译报错信息(没有实现display):
error[E0277]: `Rect` doesn't implement `std::fmt::Display`
   --> src\main.rs:114:20
    |
114 |     println!("{}", rect);
    |                    ^^^^ `Rect` cannot be formatted with the default formatter
    |
    = help: the trait `std::fmt::Display` is not implemented for `Rect`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
*/
}

fn get_area_struct(rect: &Rect) -> u32 { // 使用结构体的不可变的借用即可,无需获得所有权
    rect.w * rect.l
}

注释中附带的编译报错信息,是指Rect结构体没有实现打印trait std::fmt::Display,不清楚如何打印。其他基础类型能答应是因rust默认实现了这些类型的display trait,而自定义类型无默认实现。
但下一行提示可使用{:?} (or {:#?} for pretty-print)来代替。

struct Rect {
    w: u32,
    l: u32,
}

fn main() {
    let rect = Rect {
        w: 30,
        l: 40,
    };
    // 传入不可变的引用,传入后为不可变借用,不会改变变量的所有权,所有权依然归属于main函数
    println!("{}", get_area_struct(&rect));
    println!("{:?}", rect);
/* 加上{:?}后打印,编译报错信息(变成了没有实现debug):
error[E0277]: `Rect` doesn't implement `Debug`
   --> src\main.rs:114:22
    |
114 |     println!("{:?}", rect);
    |                      ^^^^ `Rect` cannot be formatted using `{:?}`
    |
    = help: the trait `Debug` is not implemented for `Rect`
    = note: add `#[derive(Debug)]` to `Rect` or manually `impl Debug for Rect`
*/
}

fn get_area_struct(rect: &Rect) -> u32 { // 使用结构体的不可变的借用即可,无需获得所有权
    rect.w * rect.l
}

错误信息变成了没有实现debug,debug和display一样,也是一种格式化的方法。下一行提示可加上#[derive(Debug)]或者手动实现debug。这是因为rust默认包含了打印调试信息的功能,但需要显示添加,能直接打印。

#[derive(Debug)] // 需要显示加上打印调试信息
struct Rect {
    w: u32,
    l: u32,
}

fn main() {
    let rect = Rect {
        w: 30,
        l: 40,
    };
    // 传入不可变的引用,传入后为不可变借用,不会改变变量的所有权,所有权依然归属于main函数
    println!("{}", get_area_struct(&rect));
    println!("{:?}", rect); // 一行打印
    println!("\n{:#?}", rect); // 加上#后换行打印
}

fn get_area_struct(rect: &Rect) -> u32 { // 使用结构体的不可变的借用即可,无需获得所有权
    rect.w * rect.l
}
/* 打印输出:
Rect { w: 30, l: 40 }

Rect {
    w: 30,
    l: 40,
}
 */

这是因为rust提供了很多默认的trait(已实现好的函数方法),可用于derive,#[derive(Debug)]中的derive是派生的意思,可利用现有trait,给自定义类型添加很多功能。#[derive(Debug)]整行的意思是让Rect派生于Debug trait,所以才可直接用debug模式打印信息。