陪老 K 学 Rust (八)
自定义数据类型:结构体
Rust 的结构体和枚举有一些新的特性,主要涉及到关联值、解构,模式匹配和解构。使用结构体是 Rust 中定义新数据类型的唯一方式,结构体的定义方式有两种:
- 元组形式
- 记录形式
除了可以访问内部字段以外,两种结构体都支持解构其字段(如果有的话)。
1 基本形式
元组和记录形式的基本定义和基本访问形式见如下代码段。
#[derive(Debug)]
struct NoFieldTuple;
#[derive(Debug)]
struct OneFieldTuple(i32);
#[derive(Debug)]
struct TwoFieldTuple(i32, u32);
#[derive(Debug)]
struct OneFieldRecord {
index: u32,
}
#[derive(Debug)]
struct TwoFieldRecord {
index: u32,
value: i32,
}
fn main() {
let no_field_tuple = NoFieldTuple;
println!("{:?}", no_field_tuple);
let one_field_tuple = OneFieldTuple(1);
println!("{:?}", one_field_tuple);
let mut two_field_tuple = TwoFieldTuple(2, 3);
println!("{:?}", two_field_tuple);
two_field_tuple.0 = 4;
two_field_tuple.1 = 5;
println!("{:?}", two_field_tuple);
let one_field_record = OneFieldRecord{index: 0};
println!("{:?}", one_field_record);
let mut two_field_record = TwoFieldRecord{index: 1, value: 3,};
println!("{:?}", two_field_record);
two_field_record.value = 4;
println!("{:?}", two_field_record);
}
NoFieldTuple
OneFieldTuple(1)
TwoFieldTuple(2, 3)
TwoFieldTuple(4, 5)
OneFieldRecord { index: 0 }
TwoFieldRecord { index: 1, value: 3 }
TwoFieldRecord { index: 1, value: 4 }
元组和记录的区别:
- 元组形式可以用空元组来定义结构,而记录形式不可以。
- 元组使用索引来访问字段,记录使用标签来访问字段。
2 解构
理解了元组的解构以后,元组结构的解构就比较容易理解了,一一对应即可。
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
struct Foobar(i32, u32);
struct Greet {
f1: i32,
f2: u32,
}
fn main() {
let foobar0 = Foobar(1, 2);
let Foobar(x, y) = foobar0;
print_type_of(&x);
print_type_of(&y);
let greet0 = Greet{f1: 1, f2: 2};
let Greet{f1: v1, f2: v2} = greet0;
print_type_of(&v1);
print_type_of(&v2);
let greet1 = Greet{f1: 3, f2: 4};
let Greet{f1: f1, f2: f2} = greet1;
print_type_of(&f1);
print_type_of(&f2);
let greet1 = Greet{f1: 5, f2: 6};
let Greet{f1, f2} = greet1;
print_type_of(&f1);
print_type_of(&f2);
}
编译输出和运行输出:
warning: the `f1:` in this pattern is redundant
--> r35.rs:23:15
|
23 | let Greet{f1: f1, f2: f2} = greet1;
| ---^^^
| |
| help: remove this
|
= note: `#[warn(non_shorthand_field_patterns)]` on by default
warning: the `f2:` in this pattern is redundant
--> r35.rs:23:23
|
23 | let Greet{f1: f1, f2: f2} = greet1;
| ---^^^
| |
| help: remove this
i32
u32
i32
u32
i32
u32
i32
u32
元组形式的解构和记录形式的解构形式上是类似的。
对于记录形式的结构,在字段名称和解构变量名称一致的情况下,
let Greet{f1: f1, f2: f2} = greet1;
这种形式简写为:let Greet{f1, f2} = greet1;
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
struct Foobar(i32, u32);
struct Greet {
f1: i32,
f2: u32,
}
fn main() {
let mut foobar = Foobar(1, 2);
let Foobar(mut e1, ref mut e2) = foobar;
println!("e1: {}, e2: {}", e1, e2);
print_type_of(&e1);
print_type_of(&e2);
e1 = 0;
println!("new e1: {}", e1);
let greet = Greet{f1: 3, f2: 4};
let Greet{ref f1, mut f2} = greet;
println!("f1: {}, f2: {}", f1, f2);
f2 = 0;
println!("new f2: {}", f2);
}
运行输出:
e1: 1, e2: 2
i32
&mut u32
new e1: 0
f1: 3, f2: 4
new f2: 0
- 元组结构使用
Type()
的方式解构,与构造时的语法对应。- 记录结构使用
Type{}
的方式解构,与构造时的语法对应。
3 模式匹配与解构
- 元组结构使用
()
进行匹配和解构,与构造时的语法对应。- 记录结构使用
{}
进行匹配和解构,与构造时的语法对应。
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
struct Foobar(i32, u32);
struct Greet {
f1: i32,
f2: u32,
}
fn main() {
let foobar = Foobar(0, 1);
match foobar {
Foobar(ref x0, y@0..=1) => {
println!("match Foobar(ref x0, y@0..1) x0 is: {}, y1 is: {}", x0, y);
},
Foobar(_, y1) if y1 == 2 => {
println!("match Foobar(_, y1) if y1 == 2, y1 is: {}", y1);
print_type_of(&y1);
},
_ => {
println!("default match");
},
}
let greet = Greet{f1: 2, f2: 4};
match greet {
Greet{f1: v1, f2: v2} => {
println!("match Greet{{f1: v1, f2: v2}}, v1: {}, v2: {}", v1, v2);
print_type_of(&v1);
print_type_of(&v2);
},
}
let greet = Greet{f1: 5, f2: 6};
match greet {
Greet{f1, f2} => {
println!("match Greet{{f1, f2}}, f1: {}, f2: {}", f1, f2);
print_type_of(&f1);
print_type_of(&f2);
},
}
let greet = Greet{f1: 7, f2: 8};
match greet {
Greet{f1, f2} if f1 < 0 => {
println!("match Greet{{f1, f2}}, f1: {}, f2: {}", f1, f2);
print_type_of(&f1);
print_type_of(&f2);
},
_ => {
println!("match _");
}
}
let greet = Greet{f1: 7, f2: 8};
match greet {
Greet{f1: v0@ 0..= 10, f2: v1 @ 0 ..= 10} => {
println!("match Greet{{f1, f2}}, f1: {}, f2: {}", v0, v1);
print_type_of(&v0);
print_type_of(&v1);
},
_ => {
println!("match _");
}
}
}
运行输出:
match Foobar(ref x0, y@0..1) x0 is: 0, y1 is: 1
match Greet{f1: v1, f2: v2}, v1: 2, v2: 4
i32
u32
match Greet{f1, f2}, f1: 5, f2: 6
i32
u32
match _
match Greet{f1, f2}, f1: 7, f2: 8
i32
u32
4 模式匹配与绑定
在使用 @
绑定时,记录结构必须重新绑定新的变量名称。
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
struct Greet {
f1: i32,
f2: u32,
}
fn main() {
let greet = Greet{f1: 1, f2: 2};
match greet {
Greet{f1@0..=10, f2@0..=10} => {
println!("match Greet{{f1, f2}}, f1: {}, f2: {}", f1, f2);
print_type_of(&f1);
print_type_of(&f2);
},
_ => {
},
}
}
编译错误:
error: expected `,`
--> r42.rs:13:15
|
13 | Greet{f1@0..=10, f2@0..=10} => {
| ^^
error[E0425]: cannot find value `f1` in this scope
--> r42.rs:14:63
|
14 | println!("match Greet{{f1, f2}}, f1: {}, f2: {}", f1, f2);
| ^^ not found in this scope
error[E0425]: cannot find value `f2` in this scope
--> r42.rs:14:67
|
14 | println!("match Greet{{f1, f2}}, f1: {}, f2: {}", f1, f2);
| ^^ not found in this scope
error[E0425]: cannot find value `f1` in this scope
--> r42.rs:15:28
|
15 | print_type_of(&f1);
| ^^ not found in this scope
error[E0425]: cannot find value `f2` in this scope
--> r42.rs:16:28
|
16 | print_type_of(&f2);
| ^^ not found in this scope
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0425`.