mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
commit
8357370952
@ -2,5 +2,5 @@ fn main() {
|
||||
let condition = true;
|
||||
let number = if condition { 5 } else { 6 };
|
||||
|
||||
println!("The value of number is: {}", number);
|
||||
println!("The value of number is: {number}");
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ fn main() {
|
||||
let mut number = 3;
|
||||
|
||||
while number != 0 {
|
||||
println!("{}!", number);
|
||||
println!("{number}!");
|
||||
|
||||
number -= 1;
|
||||
}
|
||||
|
@ -2,6 +2,6 @@ fn main() {
|
||||
let a = [10, 20, 30, 40, 50];
|
||||
|
||||
for element in a {
|
||||
println!("the value is: {}", element);
|
||||
println!("the value is: {element}");
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ error[E0384]: cannot assign twice to immutable variable `x`
|
||||
| |
|
||||
| first assignment to `x`
|
||||
| help: consider making this binding mutable: `mut x`
|
||||
3 | println!("The value of x is: {}", x);
|
||||
3 | println!("The value of x is: {x}");
|
||||
4 | x = 6;
|
||||
| ^^^^^ cannot assign twice to immutable variable
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
x = 6;
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let mut x = 5;
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
x = 6;
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ fn main() {
|
||||
|
||||
{
|
||||
let x = x * 2;
|
||||
println!("The value of x in the inner scope is: {}", x);
|
||||
println!("The value of x in the inner scope is: {x}");
|
||||
}
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
fn main() {
|
||||
// 加法
|
||||
// addition
|
||||
let sum = 5 + 10;
|
||||
|
||||
// 减法
|
||||
// subtraction
|
||||
let difference = 95.5 - 4.3;
|
||||
|
||||
// 乘法
|
||||
// multiplication
|
||||
let product = 4 * 30;
|
||||
|
||||
// 除法
|
||||
// division
|
||||
let quotient = 56.7 / 32.2;
|
||||
let floored = 2 / 3; // 结果为 0
|
||||
let floored = 2 / 3; // Results in 0
|
||||
|
||||
// 取余
|
||||
// remainder
|
||||
let remainder = 43 % 5;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
fn main() {
|
||||
let t = true;
|
||||
|
||||
let f: bool = false; // 显式指定类型注解
|
||||
let f: bool = false; // with explicit type annotation
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
fn main() {
|
||||
let c = 'z';
|
||||
let z = 'ℤ';
|
||||
let z: char = 'ℤ'; // with explicit type annotation
|
||||
let heart_eyed_cat = '😻';
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ fn main() {
|
||||
|
||||
let (x, y, z) = tup;
|
||||
|
||||
println!("The value of y is: {}", y);
|
||||
println!("The value of y is: {y}");
|
||||
}
|
||||
|
@ -18,8 +18,5 @@ fn main() {
|
||||
|
||||
let element = a[index];
|
||||
|
||||
println!(
|
||||
"The value of the element at index {} is: {}",
|
||||
index, element
|
||||
);
|
||||
println!("The value of the element at index {index} is: {element}");
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ fn main() {
|
||||
}
|
||||
|
||||
fn another_function(x: i32) {
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
@ -3,5 +3,5 @@ fn main() {
|
||||
}
|
||||
|
||||
fn print_labeled_measurement(value: i32, unit_label: char) {
|
||||
println!("The measurement is: {}{}", value, unit_label);
|
||||
println!("The measurement is: {value}{unit_label}");
|
||||
}
|
||||
|
@ -8,14 +8,13 @@ error: expected expression, found statement (`let`)
|
||||
|
|
||||
= note: variable declaration using `let` is a statement
|
||||
|
||||
error[E0658]: `let` expressions in this position are experimental
|
||||
error[E0658]: `let` expressions in this position are unstable
|
||||
--> src/main.rs:2:14
|
||||
|
|
||||
2 | let x = (let y = 6);
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= 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>`
|
||||
|
||||
warning: unnecessary parentheses around assigned value
|
||||
--> src/main.rs:2:13
|
||||
|
@ -4,5 +4,5 @@ fn main() {
|
||||
x + 1
|
||||
};
|
||||
|
||||
println!("The value of y is: {}", y);
|
||||
println!("The value of y is: {y}");
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ fn five() -> i32 {
|
||||
fn main() {
|
||||
let x = five();
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
||||
fn plus_one(x: i32) -> i32 {
|
||||
|
@ -1,7 +1,7 @@
|
||||
fn main() {
|
||||
let x = plus_one(5);
|
||||
|
||||
println!("The value of x is: {}", x);
|
||||
println!("The value of x is: {x}");
|
||||
}
|
||||
|
||||
fn plus_one(x: i32) -> i32 {
|
||||
|
@ -3,5 +3,5 @@ fn main() {
|
||||
|
||||
let number = if condition { 5 } else { "six" };
|
||||
|
||||
println!("The value of number is: {}", number);
|
||||
println!("The value of number is: {number}");
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
fn main() {
|
||||
let mut count = 0;
|
||||
'counting_up: loop {
|
||||
println!("count = {}", count);
|
||||
println!("count = {count}");
|
||||
let mut remaining = 10;
|
||||
|
||||
loop {
|
||||
println!("remaining = {}", remaining);
|
||||
println!("remaining = {remaining}");
|
||||
if remaining == 9 {
|
||||
break;
|
||||
}
|
||||
@ -17,5 +17,5 @@ fn main() {
|
||||
|
||||
count += 1;
|
||||
}
|
||||
println!("End count = {}", count);
|
||||
println!("End count = {count}");
|
||||
}
|
||||
|
@ -9,5 +9,5 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
println!("The result is {}", result);
|
||||
println!("The result is {result}");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
for number in (1..4).rev() {
|
||||
println!("{}!", number);
|
||||
println!("{number}!");
|
||||
}
|
||||
println!("LIFTOFF!!!");
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
## 变量和可变性
|
||||
|
||||
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/main/src/ch03-01-variables-and-mutability.md)
|
||||
> <br>
|
||||
> commit 059f85014f2a96b7a2dcdc23e01c87ae319873bc
|
||||
>
|
||||
> commit 54164e99f7a1ad27fc6fc578783994513abd988d
|
||||
|
||||
正如第二章中[“使用变量储存值”][storing-values-with-variables]<!-- ignore --> 部分提到的那样,变量默认是不可改变的(immutable)。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 为何及如何鼓励你利用不可变性,以及何时你会选择不使用不可变性。
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
Rust 编译器保证,如果声明一个值不会变,它就真的不会变,所以你不必自己跟踪它。这意味着你的代码更易于推导。
|
||||
|
||||
不过可变性也是非常有用的,可以用来更方便地编写代码。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 `mut` 来使其可变。`mut` 也向读者表明了其他代码将会改变这个变量值的意图。
|
||||
不过可变性也是非常有用的,可以用来更方便地编写代码。尽管变量默认是不可变的,你仍然可以在变量名前添加 `mut` 来使其可变,正如在第二章所做的那样。`mut` 也向读者表明了其他代码将会改变这个变量值的意图。
|
||||
|
||||
例如,让我们将 *src/main.rs* 修改为如下代码:
|
||||
|
||||
@ -46,7 +46,7 @@ Rust 编译器保证,如果声明一个值不会变,它就真的不会变,
|
||||
{{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}}
|
||||
```
|
||||
|
||||
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。
|
||||
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。是否让变量可变的最终决定权仍然在你,取决于在某个特定情况下,你是否认为变量可变会让代码更加清晰明了。
|
||||
|
||||
### 常量
|
||||
|
||||
@ -72,9 +72,9 @@ const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
||||
|
||||
将遍布于应用程序中的硬编码值声明为常量,能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。
|
||||
|
||||
### 隐藏(Shadowing)
|
||||
### 隐藏
|
||||
|
||||
正如在[第二章][comparing-the-guess-to-the-secret-number]猜猜看游戏中所讲,我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 **隐藏** 了,这意味着程序使用这个变量时会看到第二个值。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示:
|
||||
正如在[第二章][comparing-the-guess-to-the-secret-number]猜数字游戏中所讲,我们可以定义一个与之前变量同名的新变量。Rustacean 们称之为第一个变量被第二个 **隐藏(Shadowing)** 了,这意味着当您使用变量的名称时,编译器将看到第二个变量。实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 `let` 关键字来多次隐藏,如下所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
@ -82,7 +82,7 @@ const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}}
|
||||
```
|
||||
|
||||
这个程序首先将 `x` 绑定到值 `5` 上。接着通过 `let x =` 隐藏 `x`,获取初始值并加 `1`,这样 `x` 的值就变成 `6` 了。然后,在内部作用域内,第三个 `let` 语句也隐藏了 `x`,将之前的值乘以 `2`,`x` 得到的值是 `12`。当该作用域结束时,内部 shadowing 的作用域也结束了,`x` 又返回到 `6`。运行这个程序,它会有如下输出:
|
||||
这个程序首先将 `x` 绑定到值 `5` 上。接着通过 `let x =` 创建了一个新变量 `x`,获取初始值并加 `1`,这样 `x` 的值就变成 `6` 了。然后,在使用花括号创建的内部作用域内,第三个 `let` 语句也隐藏了 `x` 并创建了一个新的变量,将之前的值乘以 `2`,`x` 得到的值是 `12`。当该作用域结束时,内部 shadowing 的作用域也结束了,`x` 又返回到 `6`。运行这个程序,它会有如下输出:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch03-02-data-types.md](https://github.com/rust-lang/book/blob/main/src/ch03-02-data-types.md)
|
||||
> <br>
|
||||
> commit 1b8746013079f2e2ce1c8e85f633d9769778ea7f
|
||||
> commit 4284e160715917a768d25265daf2db897c683065
|
||||
|
||||
在 Rust 中,每一个值都属于某一个 **数据类型**(*data type*),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
let guess: u32 = "42".parse().expect("Not a number!");
|
||||
```
|
||||
|
||||
这里如果不添加类型注解,Rust 会显示如下错误,这说明编译器需要我们提供更多信息,来了解我们想要的类型:
|
||||
如果不像上面这样添加类型注解 `: u32`,Rust 会显示如下错误,这说明编译器需要我们提供更多信息,来了解我们想要的类型:
|
||||
|
||||
```console
|
||||
{{#include ../listings/ch03-common-programming-concepts/output-only-01-no-type-annotations/output.txt}}
|
||||
@ -154,9 +154,9 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}}
|
||||
```
|
||||
|
||||
这个程序创建了一个元组,`x`,并接着使用索引为每个元素创建新变量。跟大多数编程语言一样,元组的第一个索引值是 0。
|
||||
这个程序创建了一个元组,`x`,然后使用其各自的索引访问元组中的每个元素。跟大多数编程语言一样,元组的第一个索引值是 0。
|
||||
|
||||
没有任何值的元组 `()` 是一种特殊的类型,只有一个值,也写成 `()` 。该类型被称为 **单元类型**(*unit type*),而该值被称为 **单元值**(*unit value*)。如果表达式不返回任何其他值,则会隐式返回单元值。
|
||||
不带任何值的元组有个特殊的名称,叫做 **单元(unit)** 元组。这种值以及对应的类型都写作 `()`,表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。
|
||||
|
||||
#### 数组类型
|
||||
|
||||
@ -226,7 +226,7 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
程序在索引操作中使用一个无效的值时导致 **运行时** 错误。程序带着错误信息退出,并且没有执行最后的 `println!` 语句。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度,Rust 会 *panic*,这是 Rust 术语,它用于程序因为错误而退出的情况。这种检查必须在运行时进行,特别是在这种情况下,因为编译器不可能知道用户在以后运行代码时将输入什么值。
|
||||
|
||||
这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中,并没有进行这类检查,这样当提供了一个不正确的索引时,就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行,Rust 让你避开此类错误。第九章会讨论更多 Rust 的错误处理。
|
||||
这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中,并没有进行这类检查,这样当提供了一个不正确的索引时,就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行,Rust 让你避开此类错误。第九章会更详细地讨论 Rust 的错误处理机制,以及如何编写可读性强而又安全的代码,使程序既不会 panic 也不会导致非法内存访问。
|
||||
|
||||
[comparing-the-guess-to-the-secret-number]:
|
||||
ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch03-03-how-functions-work.md](https://github.com/rust-lang/book/blob/main/src/ch03-03-how-functions-work.md)
|
||||
> <br>
|
||||
> commit 3cb562efb67fd5b57c0b20c316cbb8179133e196
|
||||
> commit 4284e160715917a768d25265daf2db897c683065
|
||||
|
||||
函数在 Rust 代码中非常普遍。你已经见过语言中最重要的函数之一:`main` 函数,它是很多程序的入口点。你也见过 `fn` 关键字,它用来声明新函数。
|
||||
|
||||
@ -16,7 +16,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
|
||||
我们在Rust 中通过输入 `fn` 后面跟着函数名和一对圆括号来定义函数。大括号告诉编译器哪里是函数体的开始和结尾。
|
||||
|
||||
可以使用函数名后跟圆括号来调用我们定义过的任意函数。因为程序中已定义 `another_function` 函数,所以可以在 `main` 函数中调用它。注意,源码中 `another_function` 定义在 `main` 函数 **之后**;也可以定义在之前。Rust 不关心函数定义于何处,只要定义了就行。
|
||||
可以使用函数名后跟圆括号来调用我们定义过的任意函数。因为程序中已定义 `another_function` 函数,所以可以在 `main` 函数中调用它。注意,源码中 `another_function` 定义在 `main` 函数 **之后**;也可以定义在之前。Rust 不关心函数定义所在的位置,只要函数被调用时出现在调用之处可见的作用域内就行。
|
||||
|
||||
让我们新建一个叫做 *functions* 的二进制项目来进一步探索函数。将上面的 `another_function` 例子写入 *src/main.rs* 中并运行。你应该会看到如下输出:
|
||||
|
||||
@ -44,9 +44,9 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
|
||||
{{#include ../listings/ch03-common-programming-concepts/no-listing-17-functions-with-parameters/output.txt}}
|
||||
```
|
||||
|
||||
`another_function` 的声明中有一个命名为 `x` 的参数。`x` 的类型被指定为 `i32`。当我们将 `5` 传给 `another_function` 时,`println!` 宏将 `5` 放入格式化字符串中大括号的位置。
|
||||
`another_function` 的声明中有一个命名为 `x` 的参数。`x` 的类型被指定为 `i32`。当我们将 `5` 传给 `another_function` 时,`println!` 宏会把 `5` 放在格式字符串中包含 `x` 的那对花括号的位置。
|
||||
|
||||
在函数签名中,**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器再也不需要你在代码的其他地方注明类型来指出你的意图。
|
||||
在函数签名中,**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器再也不需要你在代码的其他地方注明类型来指出你的意图。而且,在知道函数需要什么类型后,编译器就能够给出更有用的错误消息。
|
||||
|
||||
当定义多个参数时,使用逗号分隔,像这样:
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/main/src/ch03-05-control-flow.md)
|
||||
> <br>
|
||||
> commit 1b8746013079f2e2ce1c8e85f633d9769778ea7f
|
||||
> commit 4284e160715917a768d25265daf2db897c683065
|
||||
|
||||
根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码的能力是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。
|
||||
|
||||
@ -156,6 +156,18 @@ again!
|
||||
|
||||
我们在猜谜游戏中也使用了 `continue`。循环中的 `continue` 关键字告诉程序跳过这个循环迭代中的任何剩余代码,并转到下一个迭代。
|
||||
|
||||
#### 从循环返回值
|
||||
|
||||
`loop` 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 `break` 表达式,它会被停止的循环返回:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}}
|
||||
```
|
||||
|
||||
在循环之前,我们声明了一个名为 `counter` 的变量并初始化为 `0`。接着声明了一个名为 `result` 来存放循环的返回值。在循环的每一次迭代中,我们将 `counter` 变量加 `1`,接着检查计数是否等于 `10`。当相等时,使用 `break` 关键字返回值 `counter * 2`。循环之后,我们通过分号结束赋值给 `result` 的语句。最后打印出 `result` 的值,也就是 20。
|
||||
|
||||
#### 循环标签:在多个循环之间消除歧义
|
||||
|
||||
如果存在嵌套循环,`break` 和 `continue` 应用于此时最内层的循环。你可以选择在一个循环上指定一个 **循环标签**(*loop label*),然后将标签与 `break` 或 `continue` 一起使用,使这些关键字应用于已标记的循环而不是最内层的循环。下面是一个包含两个嵌套循环的示例
|
||||
|
||||
```rust
|
||||
@ -168,16 +180,6 @@ again!
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/output.txt}}
|
||||
```
|
||||
|
||||
#### 从循环返回值
|
||||
|
||||
`loop` 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 `break` 表达式,它会被停止的循环返回:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}}
|
||||
```
|
||||
|
||||
在循环之前,我们声明了一个名为 `counter` 的变量并初始化为 `0`。接着声明了一个名为 `result` 来存放循环的返回值。在循环的每一次迭代中,我们将 `counter` 变量加 `1`,接着检查计数是否等于 `10`。当相等时,使用 `break` 关键字返回值 `counter * 2`。循环之后,我们通过分号结束赋值给 `result` 的语句。最后打印出 `result` 的值,也就是 20。
|
||||
|
||||
#### `while` 条件循环
|
||||
|
||||
在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 `break` 停止循环。这个循环类型可以通过组合 `loop`、`if`、`else` 和 `break` 来实现;如果你喜欢的话,现在就可以在程序中试试。
|
||||
|
Loading…
Reference in New Issue
Block a user