0%

Rust 学习笔记:第5章 struct

定义并实例化 struct

struct 中文名称是 结构体,可以自定义数据类型,为相关联的值命名,并打包成有意义的组合

定义 struct

  • 使用 struct 关键字,并为整个 struct 命名
  • 在花括号内,为所有字段(Field)定义名称与类型
1
2
3
4
5
6
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}

实例化 struct

  • 想要使用 struct,需要创建 struct 的实例
    • 为每个字段指定具体值
    • 无需按声明的顺序进行指定
1
2
3
4
5
6
let user1 = User{
email: String::from("some@example.com"),
usernmae: String::from("someusernmae123"),
active: true,
sign_in_count: 1,
};

取得 struct 里面某个值

  • 使用点标记法

为 user 的某个字段赋予新值,需要在实例化时为 user 加上 mut 关键字,然后使用点标记就可以赋值

一旦 struct 的实例是可变的,那么实例中所有的字段都是可变的

1
2
3
4
5
6
7
8
let mut user1 = User{
email: String::from("some@example.com"),
usernmae: String::from("someusernmae123"),
active: true,
sign_in_count: 1,
};

user1.email = String::from("anotheremail@example.com");

struct 作为函数的返回值

1
2
3
4
5
6
7
8
fn build_user(email: String, username: String) -> User {
User{
email: email,
usernmae: username,
active: true,
sign_in_count: 1,
}
}

字段初始化简写

  • 当字段名与字段值对应变量相同时,就可以使用该字段初始化简写方式
1
2
3
4
5
6
7
8
fn build_user(email: String, usernmae: String) -> User {
User{
email,
username,
active: true,
sign_in_count: 1,
}
}

struct 更新语法

  • 当你基于某个 struct 实例来创建一个新实例的时候,可以使用 struct 更新语法

不使用更新语法

1
2
3
4
5
6
let user2 = User{
email: String::from("another@axmple.com"),
username: String::from("anotherusername567"),
active: user1.active,
sign_in_count: user1.sign_in_count,
};

使用更新语法

1
2
3
4
5
let user2 = User {
email: String::from("another@axmple.com"),
username: String::from("anotherusername567"),
..user1
};

Tuple struct

  • 可定义类似 tuple 的 struct,叫做 tuple struct
    • tuple struct 整体有个名,但里面的元素没有名
    • 适用:想给整个 tuple 起名,并让它不同于其它 tuple,而且又不需要给每个元素起名
  • 定义 tuple struct: 使用 struct 关键字,后面是名字,以及里面元素的类型

black 和 origin 是不同的类型,是不同 tuple struct 的实例

1
2
3
4
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let orign = Ponit(0, 0, 0);

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

  • 可以定义没有任何字段的 struct,叫做 Unit-Like struct(因为与(), 单元类型类似)
  • 适用于需要在某个类型上实现某个 trait,但是在里面又没有想要存储的数据

struct 数据的所有权

1
2
3
4
5
6
struct User{
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
  • 这里的字段使用了 String 而不是 &str

    • 该 struct 实例拥有其所有的数据
    • 只要 struct 实例是有效的,那么里面的字段数据也是有效的
  • struct 里也可以存放引用,但这需要使用生命周期

    • 生命周期保证只要 struct 实例是有效的,那么里面的引用也是有效的
    • 如果 struct 里面存储引用,而不使用生命周期,就会报错
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      struct User{
      username: &str,
      email: &str,
      sign_in_count: u64,
      active: bool,
      }

      fn main() {
      println!("hello, world");

      let user1 = User {
      email: "fdas",
      usernmae: "fda",
      active: true,
      sign_in_count: 556
      };
      }

struct 例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

fn main() {
let rect = Rectangle {
width: 30,
length: 50
};

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

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

struct 方法

  • 方法和函数类似:fn 关键字、名称、参数、返回值
  • 方法与函数不同之处
    • 方法是在struct(或enum、trait对象)的上下文中定义
    • 第一个参数是 self,表示方法被调用的 struct 实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.length
}
}

fn main() {
let rect = Rectangle {
width: 30,
length: 50
};

println!("{}", rect.area());
println!("{:#?}", rect);
}
  • impl 块里定义方法
  • 方法的第一个参数可以是 &self,也可以获得其所有权或可变借用(&mut self),和其他参数一样

方法调用的运算符

  • C/C++:object->something() 和 (*object).something() 一样
  • Rust 没有 -> 运算符
  • Rust 会自动引用和解引用
    • 在调用方法时就会发生这种行为
  • 在调用方法时,Rust 根据情况自动添加 &、&mut 或 *,以便 object 可以匹配方法的签名
  • 下面两行代码效果相同
    • p1.distance(&2);
    • (&p1).distance(&p2)

方法参数

  • 方法可以有多个参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.length
}

fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.length > other.length
}
}

fn main() {
let rect1 = Rectangle {
width: 30,
length: 50,
};
let rect2 = Rectangle {
width: 10,
length: 40,
};
let rect3 = Rectangle {
width: 35,
length: 55,
};

println!("{}", rect1.can_hold(&rect2));
println!("{}", rect1.can_hold(&rect3));
}

关联函数

  • 可以在 impl 快里定义不把 self 作为第一个参数的函数,他们叫关联函数(并不是方法)
    • 例如:String::from()
  • 关联函数通常用于构造器
  • ::符号
    • 关联函数
    • 模块创建的命名空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.length
}

fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.length > other.length
}

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

fn main() {
let s = Rectangle::square(20);
println!("{:#?}", s);
}

多个 impl

  • 每个 struct 允许拥有多个 impl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#[derive(Debug)]
struct Rectangle {
width: u32,
length: u32,
}

impl Rectangle {
fn area(&self) -> u32 {
self.width * self.length
}
}

impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.length > other.length
}
}

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


fn main() {
let s = Rectangle::square(20);
println!("{:#?}", s);
}