10_rust的结构体struct

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模式打印信息。

rust的方法

方法与函数类似:fn关键字、名称、参数、返回值。
方法与函数不同之处:

  • 方法是在struct(或enum、trait对象)的上下文中定义
  • 第一个参数是self,表示方法被调用的struct实例
    例子:
#[derive(Debug)] // 加上打印调试信息
struct Rect {
    w: u32,
    l: u32,
}

impl Rect {
    fn get_area_struct(&self) -> u32 { // 这里使用借用(引用),不需要所有权,也可获得所有权或可变的借用
        self.w * self.l
    }
    // fn get_area_struct(self) -> u32 {} // 获得所有权(move)
    // fn get_area_struct(&mut self) -> u32 {} // 可变借用
}

fn main() {
    let rect = Rect {
        w: 30,
        l: 40,
    };
    println!("{}", rect.get_area_struct());
    println!("{:?}", rect); // 一行打印
    println!("\n{:#?}", rect); // 加上#后换行打印
}

定义方法:在impl块里定义方法,第一个参数是&self,也可获得所有权/可变借用,和其他参数一样,能更好组织代码。
方法调用的运算符
rust会在调用方法时自动引用或解引用,根据情况自动添加&、&mut或*,以便object可匹配方法签名。
如 p1.distance(&p2) 与 (&p1).distance(&p2)效果是等价的。
方法也可增加其他参数.

关联函数

还可在impl块里定义不把self作为第一个参数的函数,叫关联函数(不是方法),不是在实例中调用,但跟类型有关联,如之前的String::from()函数,关联函数通常用于构造器。

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

impl Rect {
    fn get_area_struct(&self) -> u32 {
        self.w * self.l
    }
    fn square(size: u32) -> Rect { // 关联函数,创建一个正方形
        Rect {
          w: size,
          l: size,
        }
    }
}

fn main() {
    let s = Rect::square(10); // 使用类直接调用,相当于函数位于类型的命名空间里
    let rect = Rect {
        w: 30,
        l: 40,
    };
    println!("{}", rect.get_area_struct());
}
  • ::符号作用:调用关联函数,模块创建的命名空间。

多个impl块

每个struct允许拥有多个impl块。
如:

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

impl Rect {
    fn get_area_struct(&self) -> u32 { // 这里使用借用(引用),不需要所有权,也可获得所有权或可变的借用
        self.w * self.l
    }
    // fn get_area_struct(self) -> u32 {} // 获得所有权(move)
    // fn get_area_struct(&mut self) -> u32 {} // 可变借用
}

impl Rect {
  fn square(size: u32) -> Rect { // 关联函数,创建一个正方形
      Rect {
        w: size,
        l: size,
      }
  }
}

fn main() {
    let s = Rect::square(10); // 使用类直接调用,相当于函数位于类型的命名空间里
    let rect = Rect {
        w: 30,
        l: 40,
    };
    println!("{}", rect.get_area_struct());
    println!("{:?}", rect); // 一行打印
    println!("\n{:#?}", rect); // 加上#后换行打印
}
posted @ 2023-10-18 11:27  00lab  阅读(28)  评论(0编辑  收藏  举报