Skip to content

Struct 结构体

基本使用

自定义的数据类型,类似于 TS 的 interface。

rust
struct User {
    username: String,
    email: String,
    age: usize,
    active: bool,
} // 这里不需要分号

fn main() {
    // 如果需要 user1 里的字段可变,可以声明 let mut user1。那么里面的内容都是可以修改的。
    let user1 = User {
        username: String::from("王大锤"),
        email: String::from("xxxxxx@qq.com"),
        age: 13,
        active: true,
    };

    println!(
        "username: {}, email: {}, age: {}, active: {}",
        user1.username, user1.email, user1.age, user1.active
    );
    // username: 王大锤, email: xxxxxx@qq.com, age: 13, active: true
}

Struct 也可以作为函数的返回值,字段名和字段值对应变量名相同时,可以简写,类似于 JS

rust
fn build_user(email:String, username: String) -> User {
    User {
        email,
        username,
        age:13,
        active:true
    }
}

Struct 更新语法

rust
fn main() {
    // 如果需要 user1 里的字段可变,可以声明 let mut user1,那么里面的内容都是可以修改的。
    let mut user1 = User {
        username: String::from("王大锤"),
        email: String::from("xxxxxx@qq.com"),
        age: 13,
        active: true,
    };

    let user2 = User {
        username: String::from("大锤giegie"),
        email: String::from("xxxx@163.com"),
        ..user1
    };
}

Tuple Struct(结构体元组)

可以定义类似 Tuple 元组的 Struct。

rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let white = Color(255,255,255);
let origin = Point(0,0,0);

Struct 数据的所有权

rust
struct User {
    username: String,
    email: String,
    age: usize,
    active: bool,
}
  • 这里的字段使用了 String 而不是&str

  • 只要 struct 实例是有效的,那么里面的字段数据也是有效的

  • Struct 里也可以存放引用,但是需要使用生命周期(后面补充)

  • 如果 struct 里存放引用但不使用生命周期就会报错。

计算长方形面积 Demo

rust
//对这个结构体使用调试模式,这个注解实际上使用的是std::fmt::Debug
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 10,
        height: 10,
    };

    println!("area is {}", area(&rect));

    //这里是为了能将rect里的信息更好的打印出来,对应的结构体上面记得加#[derive(Debug)],加#号会换行
    println!("{:#?}", rect);
}

fn area(rect: &Rectangle) -> u32 {
    rect.width * rect.height
}

Struct 的方法

  • 方法和函数类似

  • 不同之处:方法在 Struct 中(或 enum、trait 对象)的上下文定义里

  • 方法第一个参数总是 self,表示方法被调用的 Struct 实例,后续跟着的是其它参数。

方法定义

  • 在 impl 块里定义方法
  • 方法第一个参数是&self 时是引用,不获得所有权,如果写 self 会获得当前结构体所有权,也可以使用 mut 关键数字实现可变借用

使用方法来重写上面的例子

rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    //引用Rectangle结构体
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect = Rectangle {
        width: 10,
        height: 10,
    };

    println!("area is {}", rect.area());

    println!("{:#?}", rect);
}

方法调用运算符

Rust 会在调用方法时自动引用或解引用。

调用方法时,Rust 会根据情况自动添加 &、&mut 或 *(解引用符号),以便 object 可以匹配到方法的签名,比如说下面两行代码效果相同:

rust
p1.distance(&p2);
(&p1).distance(&p2);

关联函数

可以在 impl 块里定义不把 self 作为第一个参数的函数,叫关联函数(不叫方法了),类似于别的语言的静态方法 static。比如我们之前用得很多的String::from()就是一个关联函数。

  • 关联函数常用于构造器

看个例子,下面就是关联函数的例子。

rust
#[derive(Debug)] //对这个结构体使用调试模式,这个注解实际上使用的是std::fmt::Debug
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect = Rectangle::square(10);
    println!("{:?}", rect); //Rectangle { width: 10, height: 10 }
}

到了现在,我们再来说说我们之前一直在使用到的两个冒号,::,这是干嘛用的呢?

  • 可以使用于关联函数
  • 模块创建的命名空间(后面讲)

多个 impl 块

每个 struct 结构体可以拥有多个 impl 块。比如同名的 impl 块,里面放不同的方法(同一种类型的抽象可以这样但没必要)。

rust
struct Test {}

impl Test {
  fn test1() {}
}

impl Test {
  fn test2() {}
}

每天进步一丢丢