pull/89/head
sunface 3 years ago
parent e1f937c3e5
commit ed3f77ce49

@ -17,25 +17,27 @@
- [变量绑定与结构](basic/variable.md) - [变量绑定与结构](basic/variable.md)
- [基本类型](basic/base-type/index.md) - [基本类型](basic/base-type/index.md)
- [数值类型](basic/base-type/numbers.md) - [数值类型](basic/base-type/numbers.md)
- [字符、布尔、元类型](basic/base-type/others.md) - [字符、布尔、元类型](basic/base-type/char-bool.md)
- [语句与表达式](basic/base-type/statement-expression.md)
- [函数 todo](basic/base-type/function.md)
- [所有权和借用](basic/ownership/index.md) - [所有权和借用](basic/ownership/index.md)
- [所有权](basic/ownership/ownership.md) - [所有权](basic/ownership/ownership.md)
- [引用与借用](basic/ownership/borrowing.md) - [引用与借用](basic/ownership/borrowing.md)
- [字符串与切片](basic/string-slice.md) - [字符串与切片](basic/string-slice.md)
- [复合类型(todo)](basic/compound-type/intro.md) - [复合类型](basic/compound-type/intro.md)
- [结构体(todo)](basic/compound-type/struct.md)
- [枚举](basic/compound-type/enum.md)
- [元组](basic/compound-type/tuple.md) - [元组](basic/compound-type/tuple.md)
- [数组](basic/compound-type/array.md) - [结构体](basic/compound-type/struct.md)
- [类型转换](basic/type-converse.md) - [枚举 todo](basic/compound-type/enum.md)
- [函数与方法(todo)](basic/function-method.md) - [数组 todo](basic/compound-type/array.md)
- [类型转换 todo](basic/type-converse.md)
- [方法Method(todo)](basic/method.md)
- [格式化输出(todo)](basic/formatted-output.md) - [格式化输出(todo)](basic/formatted-output.md)
- [流程控制(todo)](basic/flow-control.md) - [流程控制(todo)](basic/flow-control.md)
- [返回、异常和错误(todo)](basic/exception-error.md) - [返回、异常和错误(todo)](basic/exception-error.md)
- [模式匹配(todo)](basic/match-pattern.md) - [模式匹配(todo)](basic/match-pattern.md)
- [文档注释(todo)](basic/comment.md) - [文档注释(todo)](basic/comment.md)
- [包和模块(todo)](basic/crate-module.md) - [包和模块(todo)](basic/crate-module.md)
- [语句与表达式(todo)](basic/statement-expression.md)
- [进阶语法 todo](advance/intro.md) - [进阶语法 todo](advance/intro.md)
- [生命周期(todo)](advance/lifetime.md) - [生命周期(todo)](advance/lifetime.md)
@ -45,7 +47,7 @@
- [集合类型(todo)](advance/collection.md) - [集合类型(todo)](advance/collection.md)
- [函数式编程(todo)](advance/functional-programing.md) - [函数式编程(todo)](advance/functional-programing.md)
- [智能指针(todo)](advance/smart-pointer.md) - [智能指针(todo)](advance/smart-pointer.md)
- [全局变量](advance/global-variable.md)
## 专题内容,每个专题都配套一个小型项目进行实践 ## 专题内容,每个专题都配套一个小型项目进行实践
@ -128,6 +130,7 @@
- [代码规范 doing](style-guide/intro.md) - [代码规范 doing](style-guide/intro.md)
- [命名规范](style-guide/naming.md) - [命名规范](style-guide/naming.md)
- [代码风格(todo)](style-guide/code.md) - [代码风格(todo)](style-guide/code.md)
- [代码标记](style-guide/mark.md)
- [Clippy](style-guide/clippy.md) - [Clippy](style-guide/clippy.md)
- [面向对象 todo](object-oriented/intro.md) - [面向对象 todo](object-oriented/intro.md)
@ -153,6 +156,9 @@
- [Benchmark性能测试(todo)](performance/benchmark.md) - [Benchmark性能测试(todo)](performance/benchmark.md)
- [减少Runtime check(todo)](performance/runtime-check.md) - [减少Runtime check(todo)](performance/runtime-check.md)
- [编译器 todo](compiler/intro.md)
- [常见属性标记](compiler/attributes.md)
- [标准库解析 todo](std/intro.md) - [标准库解析 todo](std/intro.md)
- [如何寻找你想要的内容](std/search.md) - [如何寻找你想要的内容](std/search.md)

@ -0,0 +1,39 @@
# 全局变量
在一些场景我们可能需要全局变量来简化状态共享的代码包括全局ID全局数据存储等等下面我们来一一给出对应的实现方法。
## 全局唯一ID
```rust
use std::sync::atomic::{Ordering, AtomicUsize};
struct Factory{
factory_id: usize,
}
static GLOBAL_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
// This gives large room for ids to overflow
// This code assumes that your app never would need many factories
const MAX_ID: usize = usize::MAX / 2;
fn generate_id()->usize{
// Check overflow twice to avoid growing of GLOBAL_ID_COUNTER after overflow.
let current_val = GLOBAL_ID_COUNTER.load(Ordering::Relaxed);
if current_val > MAX_ID{
panic!("Factory ids overflowed");
}
let next_id = GLOBAL_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
if next_id > MAX_ID{
panic!("Factory ids overflowed");
}
next_id
}
impl Factory{
fn new()->Self{
Self{
factory_id: generate_id()
}
}
}
```

@ -0,0 +1,108 @@
# 函数
Rust的函数我们在之前已经见过不少跟其他语言几乎没有什么区别。因此本章的学习之路将轻松和愉快骚年们请珍惜这种愉快下一章你将体验到不一样的Rust。
在函数界,有一个函数只闻其名不闻其声,可以止小孩啼,在程序界只有`hello,world!`可以与之媲美,它就是`add`函数:
```rust
fn add(i: i32, j: i32) -> i32 {
i + j
}
```
该函数如此简单,但是又是如此的五脏俱全,声明函数的关键字`fn`,函数名`add()`,参数`i`和`j`,参数类型和返回值类型都是`i32`,总之一切那么的普通,但是又那么的自信,直到你看到了下面这张图:
<img alt="" src="/img/function-01.png" class="center" />
当你看懂了这张图,其实就等于差不多完成了函数章节的学习。但是这么短的章节显然对不起读者老爷们的厚爱,所以我们来展开下。
函数有以下需要注意的点:
- 函数名和变量名使用[蛇形命名法(snake case)](../../style-guide/naming.md),例如`fn add_two() -> {}`
- 函数的位置可以随便放Rust不关心我们在哪里定义了函数只要有定义即可
- 每个函数参数都需要标注类型
## 函数参数
Rust是强类型语言因此需要你为每一个函数参数都标识出它的具体类型例如
```rust
fn main() {
another_function(5, 6.1);
}
fn another_function(x: i32, y: f32) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
```
`another_function`函数有两个参数,其中`x`是`i32`类型,`y`是`f32`类型,然后在该函数内部,打印出这两个值。这里去掉`x`或者`y`的任何一个的类型,都会报错:
```rust
fn main() {
another_function(5, 6.1);
}
fn another_function(x: i32, y) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
```
错误如下:
```console
error: expected one of `:`, `@`, or `|`, found `)`
--> src/main.rs:5:30
|
5 | fn another_function(x: i32, y) {
| ^ expected one of `:`, `@`, or `|` // 期待以下符号之一 `:`, `@`, or `|`
|
= note: anonymous parameters are removed in the 2018 edition (see RFC 1685) // 匿名参数在Rust 2018 edition中就已经移除
help: if this is a parameter name, give it a type // 如果y是一个参数名请给予它一个类型
|
5 | fn another_function(x: i32, y: TypeName) {
| ~~~~~~~~~~~
help: if this is a type, explicitly ignore the parameter name // 如果y是一个类型请使用_忽略参数名
|
5 | fn another_function(x: i32, _: y) {
| ~~~~
```
## 函数返回
在上一章节语句和表达式中我们提到在Rust中函数就是表达式因此我们可以把函数的返回值直接赋给调用者。不像有些语言会给返回值一个名称在Rust中只需要声明返回值的类型即可(在`->`之后,`{`之前})。
函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用`return`提前返回,下面的函数使用最后一条表达式来返回一个值:
```rust
fn plus_five(x:i32) -> i32 {
x + 5
}
fn main() {
let x = plus_five(5);
println!("The value of x is: {}", x);
}
```
`x + 5`是一条表达式,求值后,返回一个值,因为它是函数的最后一行,因此该表达式的值也是函数的返回值。
再来看两个重点:
1. `let x = add_five(5)`,说明我们用一个函数的返回值来初始化`x`变量因此侧面说明了在Rust中函数也是表达式 这种写法等同于`let x = 5 + 5;`
2. `x + 5`没有分号,因为它是一条表达式,这个在上一节中我们也有详细介绍
再来看一段代码,同时使用`return`和表达式作为返回值:
```rust
fn plus_or_substract(x:i32) -> i32 {
if x > 5 {
return x - 5
}
x + 5
}
fn main() {
let x = plus_or_substract(5);
println!("The value of x is: {}", x);
}
```
`plus_or_substract`函数根据传入`x`的大小来决定是做加法还是减法,若`x > 5`则通过`return`提前返回`x - 5`的值,否则返回`x + 5`的值。

@ -0,0 +1,78 @@
# 语句和表达式
Rust的函数体是由一系列语句组成然后最后由一个表达式来返回值例如
```rust
fn add_with_extra(x: i32, y: i32) -> i32 {
let x = x + 1; // 语句
let y = y + 5; // 语句
x + y // 表达式
}
```
语句会执行一些操作但是不会返回一个值,而表达式会在求值后返回一个值,因此在上述函数体的三行代码中,前两行是语句,最后一行是表达式。
对于Rust语言而言**这种基于语句和表达式的方式是非常重要的,你需要能明确的区分这两个概念**, 但是对于其它很多语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,表达式总要返回值。
在此之前,我们已经多次使用过语句和表达式, 先从语句讲起:
## 语句
```rust
let a = 8;
let b: Vec<f64> = Vec::new();
let (a, c) = ("hi", false);
```
以上都是语句,它们完成了一个具体的操作,但是并没有返回值,因此是语句。
由于`let`是语句因此不能将let语句赋值给其它值如下形式是错误的
```rust
let b = (let a = 8);
```
错误如下:
```console
error: expected expression, found statement (`let`) // 期望表达式,确发现`let`语句
--> src/main.rs:2:13
|
2 | let b = let a = 8;
| ^^^^^^^^^
|
= note: variable declaration using `let` is a statement `let`是一条语句
error[E0658]: `let` expressions in this position are experimental // 下面的`let`用法目前是试验性的,在稳定版中尚不能使用
--> src/main.rs:2:13
|
2 | let b = let a = 8;
| ^^^^^^^^^
|
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
```
以上的错误告诉我们`let`是语句,不是表达式,因此它不返回值,也就不能给其它变量赋值。但是该错误还透漏了一个重要的信息,`let`作为表达式已经是试验功能了,也许不久的将来,我们在[`stable rust`](../../appendix/rust-dev.md)下可以这样使用。
## 表达式
表达式会进行求值,然后返回一个值。例如`5 + 6`,在求值后,返回值`11`,因此它就是一条表达式。表达式可以成为语句的一部分,例如`let y= 6`中,`6`就是一个表达式,它在求值后返回一个值`6`(有些反直觉,但是确实是表达式),调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式,总之,能返回值,它就是表达式,无论简单还是复杂。例如:
```rust
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
```
其中我们把一个语句块表达式的值赋给了y语句块长这样
```rust
{
let x = 3;
x + 1
}
```
该语句块是表达式的原因是:它的最后一行是表达式,返回了`x + 1`的值,注意`x + 1`不能以分号结尾,否则就会从表达式变成语句, **表达式不能包含分号**。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会返回一个值,请牢记!

@ -1 +1,31 @@
# 枚举 # 枚举
## 枚举的一些妙用
#### 归一不同类型
在实际项目中,我们有的时候会遇到用同一个函数去处理不同类型的场景,这些类型具有相似的方法,因此你可以在这个函数中用同一套代码进行处理,
但是问题是如果将这些类型传入此函数?类型该如何统一?
例如以下代码,需要在同一个函数中处理`tcp`流和`tls`流:
```rust
func new (stream: TcpStream) {
let mut s = stream;
if tls {
s = negotiate_tls(stream)
}
// websocket是一个WebSocket<TcpStream>或者
// WebSocket<native_tls::TlsStream<TcpStream>>类型
websocket = WebSocket::from_raw_socket(
stream, ......)
}
```
因此我们需要一个类型既能支持TcpStream又能支持TlsStream此时即可借用枚举类型来实现
```rust
enum Websocket {
Tcp(Websocket<TcpStream>),
Tls(Websocket<native_tls::TlsStream<TcpStream>>),
}
```

@ -1 +1,40 @@
# 复合类型(todo) # 复合类型
行百里者半50欢迎大家来到这里虽然还不到中点但是已经不远了。如果说之前学的基础数据类型是原子那么本章将讲的数据类型可以认为是分子。
本章的重点在复合类型上,顾名思义,复合类型是由其它类型组合而来,最典型的就是结构体`struct`和枚举`enum`。例如一个2D的点`point(x,y)`似乎从两个数值类型组合而来。我们不想单独去维护这两个数值,而是希望把它们看作一个整体去认识和处理。
来看一段代码,它使用我们之前学过的内容来构建文件操作:
```rust
#![allow(unused_variables)]
type File = String;
fn open(f: &mut File) -> bool {
true
}
fn close(f: &mut File) -> bool {
true
}
#[allow(dead_code)]
fn read(f: &mut File, save_to: &mut Vec<u8>) -> ! {
unimplemented!()
}
fn main() {
let mut f1 = File::from("f1.txt");
open(&mut f1);
//read(&mut f1, &mut vec![]);
close(&mut f1);
}
```
目前阶段非常类似原型设计提供api接口但是不去实现它们。因此在这个阶段我们需要排除一些编译器噪音引入了`#![allow(unused_variables)]`属性标记,该标记会告诉编译器无视未使用的变量,不要抛出`warning`警告,具体的常见编译器属性你可以在这里查阅:[编译器属性标记](../../compiler/attributes.md).
`read`函数也非常有趣,它返回一个`!`,这个表明该函数是一个发散函数,不会返回任何值,包括`()``unimplemented!()`告诉编译器该函数尚未实现,其实主要帮助我们快速完成主要代码,回头可以通过搜索这些标记来完成次要代码,类似的还有`todo!()`.当代码执行到这种语句使,编译器会直接报错,你可以反注释`read(&mut f1, &mut vec![]);`这行,然后再尝试运行程序。
同时,从代码设计角度来看,关于文件操作的类型和函数散落的到处都是,特别是当文件属性和相关的操作多了后,更是难以管理,而且`open(&mut f1)`也远没有`f1.open()`好,因此这就是基本类型的局限性:**无法从更高的抽象层次去简化代码**。
接下来,我们将引入结构体这个高级数据结构,来看看怎么样更好的解决这类问题,开始之前,先来看看何为`元组`.

@ -1,3 +1,249 @@
# Struct(todo) # 结构体
## 结构体更新语法 在上一节,我们提到需要一个更高级的数据结构来帮助我们更好的抽象问题,而结构体`strct`恰恰就是这样的复合数据结构,它是由其它数据类型组合而来, 其它语言也有类似的数据结构,不过可能有不同的名称,例如`object`、`record`等。
结构体跟之前讲过的[元组](../base-type/tuple.md)有些相像:都是由多种类型组合而成。但是与元组不同的是,结构体可以为内部的每个字段起一个富有含义的名称。因此结构体更加灵活更加强大,你无需依赖这些字段的顺序来访问和解析它们。
## 结构体语法
天下无敌的剑士往往也因为他有一炳无双之剑,既然结构体这么强大,那么我们就需要给它配套一套强大的语法,让用户能更好的驾驭。
#### 定义结构体
定义结构体有几部分组成:
- 通过关键字`struct`定义
- 一个清晰明确的结构体`名称`
- 数个具名的结构体`字段`
例如以下结构体定义了某网站的用户:
```rust
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
```
该结构体名称是`User`拥有4个具名的字段且每个字段都有对应的类型声明例如`username`代表了用户名,是一个可变的`String`类型。
#### 创建结构体实例
为了使用上述结构体,我们需要创建`User`结构体的`实例`
```rust
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
```
有几点值得注意:
1. 初始化实例时,需要为每个字段都进行初始化
2. 初始化时的字段顺序无需按照定义的顺序来
#### 访问结构体字段
通过`.`操作符即可访问结构体实例内部的字段值,并且也可以修改它们:
```rust
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
```
需要注意的是必须要将整个结构体都声明为可变的才能修改它Rust不允许单独将某个字段标记为可变: `let mut user1 = User {...}`.
#### 简化结构体创建
先看以下这个函数:
```rust
fn build_user(email: String, username: String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
```
它接收两个字符串参数:`email`和`username`,然后使用它们来创建一个`User`结构体,并且返回。可以注意到这两行:`email: email`和`username: username`非常的扎眼因为实在太啰嗦了如果你从typscript过来肯定会鄙视Rust一番不过好在它也不是无可救药:
```rust
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
```
如上所示,当函数参数和结构体字段同名时,可以直接使用缩略的方式进行初始化,跟`typescript`中一模一样.
#### 结构体更新语法
在实际场景中,有一种情况很常见:根据已有的结构体实例,创建新的结构体实例,例如根据已有的`user1`实例来构建`user2`
```rust
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
```
老话重提如果你从typescript过来肯定觉得啰嗦爆了手动把user1的三个字段逐个赋值给user2好在Rust为我们提供了`结构体更新语法`:
```rust
let user2 = User {
email: String::from("another@example.com"),
..user1
};
```
因为`user2`仅仅在`email`上与`user1`不同,因此我们只需要对`email`进行赋值,剩下的通过结构体更新语法`..user1`即可完成。
`..`语法说明我们没有显示声明的字段全部从`user1`中自动获取。需要注意的是`..user1`必须在结构体的尾部使用。
> 结构体更新语法跟赋值语句`=`非常相像,因此在上面代码中,`user1`的部分字段所有权被转移到`user2`中:`username`字段发生了所有权转移,作为结果,`user1`无法再被使用。
>
> 聪明的读者肯定要发问了:明明有三个字段进行了自动赋值,为何只有`username`发生了所有权转移?
>
> 仔细回想一下[所有权](../ownership/ownership.md)那一节的内容我们提到了Copy特征实现了Copy特征的类型无需所有权转移可以直接在赋值时进行
> 数据拷贝,其中`bool`和`u64`类型就实现了`Copy`特征,因此`active`和`sign_in_count`字段在赋值给user2时仅仅发生了拷贝而不是所有权转移.
>
> 值的注意的是:`username`所有权被转移给了`user2`,导致了`user1`无法再被使用,但是并不代表`user1`内部的字段不能被急需使用,例如:
```rust
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
println!("{}", user1.active);
// 下面这行会报错
//println!("{}", user1);
```
## 结构体的内存排列
先看以下代码:
```rust
#[derive(Debug)]
struct File {
name: String,
data: Vec<u8>,
}
fn main() {
let f1 = File {
name: String::from("f1.txt"),
data: Vec::new(),
};
let f1_name = &f1.name;
let f1_length = &f1.data.len();
println!("{:?}", f1);
println!("{} is {} bytes long", f1_name, f1_length);
}
```
上面定义的`File`结构体在内存中的排列如下图所示:
<img alt="" src="/img/struct-01.png" class="center" />
从图中可以清晰的看出`File`结构体两个字段`name`和`data`分别拥有底层两个`[u8]`数组的所有权(`String`类型的底层也是`[u8]`数组),通过`ptr`指针指向底层数组的内存地址,这里你可以把`ptr`指针理解为Rust中的引用类型。
该图片也侧面印证了:把结构体中具有所有权的字段转移出去后,将无法再访问该字段,但是可以正常访问其它的字段.
## 元组结构体(Tuple Struct)
结构体必须要有名称,但是结构体的字段可以没有名称,这种结构体长得很像元组,因此被称为元组结构体,例如:
```rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
```
元组结构体在你希望有一个整体名称,但是又不关心里面字段的名称时将非常有用。例如上面的`Point`元组结构体众所周知3D点是`(x,y,x)`形式的坐标点,因此我们无需再为内部的字段逐一命名为:`x`,`y`,`z`。
## 元结构体(Unit-like Struct)
还记得之前讲过的基本没啥用的[元类型](../base-type/char-bool.md#元类型)吧? 元结构体就跟它很像,没有任何字段和属性,但是好在,它还挺有用。
如果你定义一个类型,但是不关心该类型的内容, 只关心它的行为时,就可以使用`元结构体`:
```rust
struct AlwaysEqual;
let subject = AlwaysEqual;
// 我们不关心为AlwaysEqual的字段数据只关心它的行为因此将它声明为元结构体然后再为它实现某个特征
impl AlwaysEqual for SomeTrait {
}
```
## 结构体数据的所有权
在之前的`User` 结构体的定义中,我们使用了自身拥有所有权的 `String` 类型而不是基于引用的`&str` 字符串切片类型。这是一个有意而为之的选择,因为我们想要这个结构体拥有它所有的数据,而不是从其它地方借用数据。
你也可以让`User`结构体从其它对象借用数据,不过这么做,就需要引入**生命周期**这个新概念(也是一个复杂的概念),简而言之,生命周期能确保结构体的作用范围要比它所借用的数据的作用范围要大。
总之,如果你想在结构体中使用一个引用,就必须加上生命周期,否则就会报错:
```rust
struct User {
username: &str,
email: &str,
sign_in_count: u64,
active: bool,
}
fn main() {
let user1 = User {
email: "someone@example.com",
username: "someusername123",
active: true,
sign_in_count: 1,
};
}
```
编译器会抱怨它需要生命周期标识符:
```console
error[E0106]: missing lifetime specifier
--> src/main.rs:2:15
|
2 | username: &str,
| ^ expected named lifetime parameter // 需要一个生命周期
|
help: consider introducing a named lifetime parameter // 考虑像下面的代码这样引入一个生命周期
|
1 ~ struct User<'a> {
2 ~ username: &'a str,
|
error[E0106]: missing lifetime specifier
--> src/main.rs:3:12
|
3 | email: &str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 ~ struct User<'a> {
2 | username: &str,
3 ~ email: &'a str,
|
```
未来在[生命周期](../../advance/lifetime.md)中会讲到如何修复这个问题以便在结构体中存储引用,不过在那之前,我们会避免在结构体中使用引用类型。

@ -1,6 +1,51 @@
# 元组 # 元组
可以使用元组返回多个值 元组也是复合类型的一种,因此它是由多种类型组合到一起形成的。元组的长度是固定的,且在声明后,无法进行伸缩。
通过以下语法可以创建一个元组:
```rust
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
```
变量`tup`被绑定了一个元组值`(500, 6.4, 1)`,该元组的类型是`(i32, f64, u8)`,看到没?元组是用括号将多个类型组合到一起,简单吧?
从元组中获取值有两种方式:
### 用模式匹配解构元组
```rust
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
```
上述代码首先创建一个元组,然后将其绑定到`tup`上,接着使用`let (x, y, z) = tup;`来完成一次模式匹配,因为元组是(n1,n2,n3)形式的,因此我们用一模一样的`(x,y,z)`形式来进行匹配,然后把元组中对应的值绑定到变量`x``y``z`上,这就是解构:用同样的形式把一个复杂对象中子值匹配出来。
### 用`.`来访问元组
模式匹配可以让我们一次性把元组全部获取出来如果想要访问某个元素那模式匹配就略显繁琐对此Rust提供了`.`的访问方式:
```rust
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
```
和其它语言的数组、字符串一样元组的索引从0开始。
### 元组的使用示例
元组在函数返回值场景很常用,例如下面的代码,可以使用元组返回多个值:
```rust ```rust
fn main() { fn main() {
let s1 = String::from("hello"); let s1 = String::from("hello");
@ -16,3 +61,8 @@ fn calculate_length(s: String) -> (String, usize) {
(s, length) (s, length)
} }
``` ```
`calculate_length`函数接收`s1`字符串的所有权,然后计算字符串的长度,接着把字符串所有权和字符串长度返回给`s2`和`len`变量。
对于其他语言元组可以用来声明一个3D点例如`Point(10,20,30)`虽然使用Rust元组也可以做到`(10,20,30)`,但是这样写有个非常重大的缺陷:
不具备任何清晰的含义,在下一章节中,会提到一种`元组结构体`,可以解决这个问题。

@ -1 +0,0 @@
# function-method.md

@ -0,0 +1,47 @@
# function-method.md
## 函数返回
SPECIAL RETURN TYPES IN RUST
If you are new to the language, some return types are difficult to interpret. These are also especially difficult to search for because they are made from symbols rather than words.
Known as the unit type, () formally is a zero-length tuple. It is used to express that a function returns no value. Functions that appear to have no return type return (), and expressions that are terminated with a semicolon (;) return (). For example, the report() function in the following code block returns the unit type implicitly:
```rust
use std::fmt::Debug;
fn report<T: Debug>(item: T) {
println!("{:?}", item);
}
```
And this example returns the unit type explicitly:
```rust
fn clear(text: &mut String) -> () {
*text = String::from("");
}
```
The unit type often occurs in error messages. Its common to forget that the last expression of a function shouldnt end with a semicolon.
The exclamation symbol, !, is known as the “Never” type. Never indicates that a function never returns, especially when it is guaranteed to crash. For example, take this code:
```rust
fn dead_end() -> ! {
panic!("you have reached a dead end");
}
```
The following example creates an infinite loop that prevents the function from returning:
```rust
fn forever() -> ! {
loop {
//...
};
}
```
As with the unit type, Never sometimes occurs within error messages. The Rust compiler complains about mismatched types when you forget to add a break in your loop block if youve indicated that the function returns a non-Never type.

@ -1 +0,0 @@
# statement-expression.md

@ -260,7 +260,6 @@ fn say_hello(s: &str) {
| |
= help: the trait `Index<{integer}>` is not implemented for `String` = help: the trait `Index<{integer}>` is not implemented for `String`
``` ```
https://rustwiki.org/en/book/ch08-02-strings.html#storing-utf-8-encoded-text-with-strings
#### 深入字符串内部 #### 深入字符串内部
字符串的底层的数据存储格式实际上是[u8],一个字节数组。对于`let hello = String::from("Hola");`这行代码来说,`hello`的长度是`4`个字节,因为`"hola"`中的每个字母在UTF8编码中仅占用1个字节但是对于下面的代码呢? 字符串的底层的数据存储格式实际上是[u8],一个字节数组。对于`let hello = String::from("Hola");`这行代码来说,`hello`的长度是`4`个字节,因为`"hola"`中的每个字母在UTF8编码中仅占用1个字节但是对于下面的代码呢?

@ -0,0 +1 @@
# 常见属性标记

BIN
src/img/.DS_Store vendored

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@ -0,0 +1,4 @@
# 代码标记
unimplemented!() todo!()

@ -0,0 +1,2 @@
https://stackoverflow.com/questions/50251487/what-are-non-lexical-lifetimes
Loading…
Cancel
Save