Skip to content

Commit fe31b24

Browse files
张东张东
authored andcommitted
补充rust类型系统
1 parent 04ca806 commit fe31b24

File tree

1 file changed

+177
-54
lines changed

1 file changed

+177
-54
lines changed

rust/types.md

Lines changed: 177 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -155,75 +155,198 @@ Rectangle { width: 0, height: 0 }
155155

156156
- **enum Attend { OnTime, Late(u32)}**: 枚举,或代数数据类型
157157

158-
枚举下的条目被叫做变体,变体可以挂载各种不同的类型
158+
声明之后他就是一种新的数据类型,枚举下的条目被叫做变体,变体可以挂载各种不同的类型
159159

160-
### 枚举的实例化
161-
枚举的实例化实际是枚举变体的实例化
160+
如果声明一个ip地址,我们可能会用到结构体
162161

163162
```rust
164-
enum WebEvent {
165-
PageLoad,
166-
PageUnload,
167-
KeyPress(char),
168-
Paste(String),
169-
Click { x: i64, y: i64 },
170-
}
171-
172-
let a = WebEvent::PageLoad;
173-
let b = WebEvent::PageUnload;
174-
let c = WebEvent::KeyPress('c');
175-
let d = WebEvent::Paste(String::from("batman"));
176-
let e = WebEvent::Click { x: 320, y: 240 };
163+
enum IpAddrKind {
164+
V4,
165+
V6,
166+
}
167+
168+
struct IpAddr {
169+
kind: IpAddrKind,
170+
address: String,
171+
}
172+
173+
let home = IpAddr {
174+
kind: IpAddrKind::V4,
175+
address: String::from("127.0.0.1"),
176+
};
177+
178+
let loopback = IpAddr {
179+
kind: IpAddrKind::V6,
180+
address: String::from("::1"),
181+
};
177182
```
183+
但是直接使用枚举可以变得更简约
184+
```rust
185+
enum IpAddr {
186+
V4(String),
187+
V6(String),
188+
}
189+
190+
let home = IpAddr::V4(String::from("127.0.0.1"));
178191

179-
可以看到,不带负载的变体实例化和带负载的变体实例化不一样。带负载的变体实例化要根据不同变体附带的类型做特定的实例化。
192+
let loopback = IpAddr::V6(String::from("::1"));
193+
```
194+
使用枚举而不是结构体还有另一个优势:每个变体可以包含不同类型和数量的关联数据。版本 4 的 IP 地址始终包含四个数值部分,其值介于 0 到 255 之间。如果我们想将V4地址存储为四个u8值,但仍将V6其表示为一个String值,那么使用结构体就无法实现。枚举可以轻松处理这种情况
180195

181-
## 类c枚举
182196
```rust
183-
// 给枚举变体一个起始数字值
184-
enum Number {
185-
Zero = 0,
186-
One,
187-
Two,
188-
}
189-
// 给枚举每个变体赋予不同的值
190-
enum Color {
191-
Red = 0xff0000,
192-
Green = 0x00ff00,
193-
Blue = 0x0000ff,
197+
enum IpAddr {
198+
V4(u8, u8, u8, u8),
199+
V6(String),
194200
}
195-
fn main() {
196-
// 使用 as 进行类型的转化
197-
println!("zero is {}", Number::Zero as i32);
198-
println!("one is {}", Number::One as i32);
199-
println!("roses are #{:06x}", Color::Red as i32);
200-
println!("violets are #{:06x}", Color::Blue as i32);
201+
202+
let home = IpAddr::V4(127, 0, 0, 1);
203+
204+
let loopback = IpAddr::V6(String::from("::1"));
205+
```
206+
207+
可以在变体中嵌入多种类型
208+
```rust
209+
enum Message {
210+
Quit,
211+
Move { x: i32, y: i32 },
212+
Write(String),
213+
ChangeColor(i32, i32, i32),
201214
}
202-
// 输出
203-
zero is 0
204-
one is 1
205-
roses are #ff0000
206-
violets are #0000ff
207215
```
208-
枚举同样能够被 impl,但是不能对枚举的变体直接 impl
216+
* Quit根本没有与之相关的数据。
217+
* Move具有命名字段,就像结构体一样。
218+
* Write包括一个String。
219+
* ChangeColor包括三个i32值。
220+
221+
也可以在枚举中定义方法
222+
223+
```rust
224+
impl Message {
225+
fn call(&self) {
226+
// method body would be defined here
227+
}
228+
}
229+
230+
let m = Message::Write(String::from("hello"));
231+
m.call();
232+
```
233+
### 枚举Option
234+
包含在 prelude 中;您无需显式地将其引入作用域。它的变体也包含在 prelude 中:您可以直接使用Some和None
235+
```rust
236+
let some_number = Some(5);//Option<i32>
237+
let some_char = Some('e');Option<char>
238+
239+
let absent_number: Option<i32> = None;//编译器无法推测出类型,所以要显示声明
240+
```
241+
当我们拥有一个Some值时,我们知道该值存在,并且该值保存在 中Some。当我们拥有一个None值时,在某种意义上,它的含义与 null 相同:我们没有有效的值
242+
243+
但是这个例子编译不了
244+
```rust
245+
let x: i8 = 5;
246+
let y: Option<i8> = Some(5);
247+
248+
let sum = x + y;//因为x,y是不同类型,自然无法相加
249+
```
250+
i8类型我们不需要担心他是一个空值,但是对于类型是`Option<i8>`我们不得不考虑他可能是个空值,所以应该把`Option<i8>`转成i8
251+
252+
为了获得可能为空的值,您必须明确选择将该值的类型设为Option<T>。然后,当您使用该值时,您需要明确处理该值为空的情况。只要值的类型不是 Option<T>,您就可以安全地假设该值不为空
209253

210254
### match
255+
好的有了枚举可以使用match来增强功能
211256
```rust
212-
fn main() {
213-
let number = 13;
214-
// 你可以试着修改上面的数字值,看看下面走哪个分支
215-
println!("Tell me about {}", number);
216-
match number {
217-
// 匹配单个数字
218-
1 => println!("One!"),
219-
// 匹配几个数字
220-
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
221-
// 匹配一个范围,左闭右闭区间
222-
13..=19 => println!("A teen"),
223-
// 处理剩下的情况
224-
_ => println!("Ain't special"),
257+
enum Coin {
258+
Penny,
259+
Nickel,
260+
Dime,
261+
Quarter,
225262
}
263+
264+
fn value_in_cents(coin: Coin) -> u8 {
265+
match coin {
266+
Coin::Penny => 1,
267+
Coin::Nickel => 5,
268+
Coin::Dime => 10,
269+
Coin::Quarter => 25,
270+
}
271+
}
272+
```
273+
match使用上很像if,但是if必须接受一个布尔值。match分支由两部分组成,一个模式和一些代码
274+
275+
如果匹配分支代码较短,我们通常不使用花括号。如果要在匹配分支中运行多行代码,则必须使用花括号
276+
277+
_ 代表默认处理下面的所以情况
278+
```rust
279+
match dice_roll {
280+
3 => add_fancy_hat(),
281+
7 => remove_fancy_hat(),
282+
_ => (),
283+
}
284+
```
285+
匹配分支的另一个有用特性是它们可以绑定到与模式匹配的值的部分。这就是我们从枚举变量中提取值的方法。
286+
287+
例如匹配`Option<T>`,可以拿到里面的i
288+
```rust
289+
match x {
290+
None => None,
291+
Some(i) => Some(i + 1),
292+
}
293+
```
294+
295+
模式匹配与数据(所有权)如何交互
296+
```rust
297+
let opt: Option<String> = Some(String::from("Hello world"));
298+
299+
match opt {
300+
Some(_) => println!("Some!"),
301+
None => println!("None!"),
302+
}
303+
304+
println!("{:?}", opt); // 这段代码可以编译通过,因为 _ 没有获取所有权。
305+
//_是一个占位符,用于忽略Some内部的值。Rust 并没有移动这个值,opt在匹配后仍然是有效的,你可以在之后使用它
306+
```
307+
```rust
308+
let opt: Option<String> = Some(String::from("Hello world"));
309+
310+
match opt {
311+
Some(s) => println!("Some: {}", s),
312+
None => println!("None!"),
313+
}
314+
315+
println!("{:?}", opt); // 这段代码不能编译通过。
316+
//Some(s)这个模式Some保留内部的String绑定到变量s上,这意味着opt内部的绑定被转移给了s。
317+
//因此,在match语句执行之后,opt丢失了对String的发票,导致无法再次访问opt,因此编译会报错。
318+
```
319+
如果您不想移动数据,但想借用数据,您可以匹配Option引用&opt,而不是直接匹配opt
320+
```rust
321+
let opt: Option<String> = Some(String::from("Hello world"));
322+
323+
match &opt { // 匹配的是 &opt(引用)
324+
Some(s) => println!("Some: {}", s),
325+
None => println!("None!"),
226326
}
327+
328+
println!("{:?}", opt); // 这段代码可以编译通过,因为我们只是借用了数据。
329+
```
330+
### if let
331+
```rust
332+
if let PATTERN = EXPR {
333+
// 当 EXPR 匹配 PATTERN 时,执行这部分代码
334+
}
335+
```
336+
if let语法允许你将if和组合let成一种更简洁的方式,以处理匹配一个模式的值,同时忽略其余值。
337+
```rust
338+
//如果使用match
339+
let config_max = Some(3u8);
340+
match config_max {
341+
Some(max) => println!("The maximum is configured to be {max}"),
342+
_ => (),
343+
}
344+
//使用if let
345+
let config_max = Some(3u8);
346+
if let Some(max) = config_max {//Some(max)是我们要匹配的模式。它表示我们希望解出包含的值,并将其绑定到变量max上
347+
println!("The maximum is configured to be {max}");
348+
}
349+
227350
```
228351
### 模式匹配
229352
模式匹配就是按对象值的结构进行匹配,并且可以取出符合模式的值。

0 commit comments

Comments
 (0)