以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正
结构体基本操作
定义、实例化
在实例化结构体时,字段内的赋值顺序可以和结构体定义不同,但是字段数量必须和定义一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| struct Person { name: String, age: u8, }
fn main() { let tom = Person { name: "tom".to_string(), age: 10, };
let jerry = Person { age: 8, name: "jerry".to_string(), }; }
|
访问、修改内部字段
需要注意的是,当我们将结构体声明为可变对象时,它下属的所有字段就都是可变的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| struct Person { name: String, age: u8, }
fn main() { let mut tom = Person { name: "tom".to_string(), age: 10, };
println!("name: {}, age: {}", tom.name, tom.age); tom.name = "jerry".to_string(); println!("name: {}, age: {}", tom.name, tom.age); }
|
基于已有实例创建新实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct Person { name: String, age: u8, species: String, }
fn main() { let tom = Person { name: "tom".to_string(), age: 10, species: "cat".to_string(), };
let jerry = Person { name: "jerry".to_string(), species: "mouse".to_string(), ..tom }; }
|
元组结构体(Tuple Struct)
在某些情况下,我们只想给元组整体取名来实现其相对于别的元组的不同。比如下面的例子中的black和origin,虽然它们内部的字段是一样的,但是他们是不同类型的结构体,这样就可以针对其类型定义它们不同的行为
1 2 3 4 5 6 7 8
| struct Color(i32, i32, i32); struct Point(i32, i32, i32);
fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0); }
|
空结构体(Unit-Like Struct)
当我们想在某个类型上只实现行为而不想在其中储存数据的时候,就可以使用空结构体
1 2 3 4 5
| struct Empty;
fn main() { let a = Empty; }
|
结构体数据的所有权
一般类型数据
如果结构中的数据储存在堆内存上的数据或者基础类型数据的话,那么这个结构体实例就拥有
其所有的数据。只要实例有效,其内部的数据就有效;反过来说,当其内部数据的所有权发生转移,实例也就失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #[derive(Debug)] struct Team { leader: String, member: String, episode: u32, }
fn main() { let msm = Team { leader: "tom".to_string(), member: "jerry".to_string(), episode: 201, };
hello(&msm.leader); println!("{:?}", msm); }
fn hello(name: &String) { println!("hello, {}", &name); }
|
引用类型数据
在结构体中也可以储存引用,但是需要使用到生命周期标注
来保证在实例有效的范围内,其内部引用也是有效的
1 2 3 4 5 6 7 8 9 10 11 12 13
| struct Team<'a> { leader: &'a str, member: &'a str, episode: u32, }
fn main() { let msm = Team { leader: "tom", member: "jerry", episode: 201, }; }
|
结构体的解构
当我们想要获得解构体内部值的时候,除了直接使用.
标记法访问结构体数据外,还可以通过模式匹配
来解构结构体实现直接获取内部值,但需要注意匹配造成的所有权转移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #[derive(Debug)] struct Rectangle { length: String, width: String, }
fn main() { let rec = Rectangle { length: 50.to_string(), width: 30.to_string(), }; let Rectangle { length, width } = rec; println!("length: {}, width: {}", length, width); println!("{:?}", rec); }
|
结构体方法
Rust中方法的作用和面向对象语言(如Python)中的方法有些类似但又有些区别,都是将行为与类型相关联,都可以访问关联类型的所有数据和方法,从而使代码更具可读性和可维护性。这里虽然标题是结构体方法,但其他的一些数据类型,比如枚举(enum)等也可以采用类似语法来实现与其关联的方法
定义方法
定义方法的语法要求
- 使用
impl
关键词进行定义
- 方法的定义在struct(或enum、trait对象)的上下文进行
- 方法的第一个参数为self,表示被方法调用的实例
- 一个类型可拥有多个
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
| struct Person { name: String, age: u8, }
impl Person { fn introduce(&self) { println!("My name is {}, {} years old!", self.name, self.age); } fn hello(&self) { println!("hello {}", self.name); } }
impl Person { fn sleep(&self) { println!("{} is sleeping", self.name); } }
fn main() { let tom = Person { name: "tom".to_string(), age: 10, };
tom.introduce(); }
|
结构体方法中实例的所有权
在上面的例子中,我们其实是使用借用的模式获得实例中的数据,但是也可以获得实例所有权或者得到实例的可变引用,这取决于用户想要实现的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #[derive(Debug)] struct Person { name: String, age: u8, }
impl Person { fn introduce(self) { println!("My name is {}, {} years old!", self.name, self.age); } }
fn main() { let tom = Person { name: "tom".to_string(), age: 10, };
tom.introduce(); println!("{:?}", tom); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #[derive(Debug)] struct Person { name: String, age: u8, }
impl Person { fn friend(&mut self) { self.name.push_str(" and jerry"); } }
fn main() { let mut tom = Person { name: "tom".to_string(), age: 10, };
println!("{:?}", tom); tom.friend(); println!("{:?}", tom); }
|
关联函数
如果学过Python知道,在类中有叫静态方法的特殊类型函数,其不以self
作为函数的第一个参数。在Rust中也有类似的函数,即关联函数,关联函数通常被用于构造器,比如下面的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| struct Rectangle { length: usize, width: usize, }
impl Rectangle { fn new(length: usize, width: usize) -> Rectangle { Rectangle { length, width, } }
fn area(&self) -> usize { self.length * self.width } }
fn main() { let rec = Rectangle::new(50, 30); println!("{}", rec.area()); }
|
在结构体方法中,self
是一个指向当前对象的指针,类似于Python定义类时的self
参数,表示当前对象。Self
是一个特殊的类型,表示当前类型本身,比如上面的例子可以这样改写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #[derive(Debug)] struct Rectangle { length: usize, width: usize, }
impl Rectangle { fn new(length: usize, width: usize) -> Self { Self { length, width, } } }
fn main() { let rec = Rectangle::new(50, 30); println!("{:?}", rec); }
|
关联函数和方法的内部调用
在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
| #[derive(Debug)] struct Rectangle { length: usize, width: usize, }
impl Rectangle { fn new(length: usize, width: usize) -> Self { Self { length, width, } }
fn area(&self) -> usize { self.length * self.width }
fn area_compare(&self, length: usize, width: usize) -> bool { let rec_new = Self::new(length, width); self.area() > rec_new.area() } }
fn main() { let rec = Rectangle::new(50, 30); println!("{:?}", rec); let e = rec.area_compare(40, 35); println!("{}", e); }
|
如果关联函数想要调用结构体方法,则需要传入结构体实例
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
| struct Rectangle { length: usize, width: usize, }
impl Rectangle { fn new(length: usize, width: usize) -> Self { Self { length, width, } }
fn area(&self) -> usize { self.length * self.width }
fn double_area(rec: &Rectangle) -> usize { let new_rec = Self::new(rec.length * 2, rec.width * 2); new_rec.area() } }
fn main() { let rec = Rectangle::new(50, 30); let double_area = Rectangle::double_area(&rec); println!("The double area of the rectangle is {} square pixels.", double_area); }
|