mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-03-21 02:10:11 +08:00
[提交翻译][tech]: 20230403.1 ⭐️⭐️ Rust Basics Series 2 Using Variables and Constants in Rust Programs (#29235)
* [翻译完成][tech]: 20230403.1 ⭐️⭐️ Rust Basics Series 2 Using Variables and Constants in Rust Programs Signed-off-by: Qian Qian "Cubik" <cubik65536@cubik65536.top> * [移动翻译][tech]: 20230403.1 ⭐️⭐️ Rust Basics Series 2 Using Variables and Constants in Rust Programs * [修复][tech]: 20230403.1 ⭐️⭐️ Rust Basics Series 2 Using Variables and Constants in Rust Programs --------- Signed-off-by: Qian Qian "Cubik" <cubik65536@cubik65536.top>
This commit is contained in:
parent
ea7ae2638a
commit
810f0cb2e8
@ -1,430 +0,0 @@
|
||||
[#]: subject: "Rust Basics Series #2: Using Variables and Constants in Rust Programs"
|
||||
[#]: via: "https://itsfoss.com/rust-variables/"
|
||||
[#]: author: "Pratham Patel https://itsfoss.com/author/pratham/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "Cubik65536"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Rust Basics Series #2: Using Variables and Constants in Rust Programs
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
In the [first chapter of the series][2], I shared my thoughts on why Rust is an increasingly popular programming language. I also showed how to [write Hello World program in Rust][2].
|
||||
|
||||
Let's continue this Rust journey. In this article, I shall introduce you to variables and constants in the Rust programming language.
|
||||
|
||||
On top of that, I will also cover a new programming concept called "shadowing".
|
||||
|
||||
### The uniqueness of Rust's variables
|
||||
|
||||
A variable in the context of a programming language (like Rust) is known as _an alias to the memory address in which some data is stored_.
|
||||
|
||||
This is true for the Rust programming language too. But Rust has one unique "feature". Every variable that you declare is **immutable by default**. This means that once a value is assigned to the variable, it can not be changed.
|
||||
|
||||
This decision was made to ensure that, by default, you don't have to make special provisions like _spin locks_ or _mutexes_ to introduce multi-threading. Rust **guarantees** safe concurrency. Since all variables (by default) are immutable, you do not need to worry about a thread changing a value unknowingly.
|
||||
|
||||
This is not to say that variables in Rust are like constants because they are not. Variables can be explicitly defined to allow mutation. Such a variable is called a **mutable variable**.
|
||||
|
||||
Following is the syntax to declare a variable in Rust:
|
||||
|
||||
```
|
||||
// immutability by default
|
||||
// the initialized value is the **only** value
|
||||
let variable_name = value;
|
||||
|
||||
// mutable variable defined by the use of 'mut' keyword
|
||||
// the initial value can be changed to something else
|
||||
let mut variable_name = value;
|
||||
```
|
||||
|
||||
> 🚧 Though you are allowed to change the value of a mutable variable, you can not assign the value of another data type to it.
|
||||
|
||||
Meaning, if you have a mutable variable of type float, you can not assign a character to it down the road.
|
||||
|
||||
### High-level overview of Rust's data types
|
||||
|
||||
In the previous article, you might have noticed that I mentioned that Rust is a strongly typed language. But to define a variable, you don't specify the data type, instead, you use a generic keyword `let`.
|
||||
|
||||
The Rust compiler can infer the data type of a variable based on the value assigned to it. But it can be done if you still wish to be explicit with data types and want to annotate the type. Following is the syntax:
|
||||
|
||||
```
|
||||
let variable_name: data_type = value;
|
||||
```
|
||||
|
||||
Some of the common data types in the Rust programming language are as follows:
|
||||
|
||||
- **Integer type**: `i32` and `u32` for signed and unsigned, 32-bit integers, respectively
|
||||
- **Floating point type**: `f32` and `f64`, 32-bit and 64-bit floating point numbers
|
||||
- **Boolean type**: `bool`
|
||||
- **Character type**: `char`
|
||||
|
||||
I will cover Rust's data types in more detail in the next article. For now, this should be sufficient.
|
||||
|
||||
> 🚧 Rust does not have implicit typecasting. So if you assign the value `8` to a variable with a floating point data type, you will face a compile time error. What you should assign instead is the value `8.` or `8.0`.
|
||||
|
||||
Rust also enforces that a variable be initialized before the value stored in it is read.
|
||||
|
||||
```
|
||||
{ // this block won't compile
|
||||
let a;
|
||||
println!("{}", a); // error on this line
|
||||
// reading the value of an **uninitialized** variable is a compile-time error
|
||||
}
|
||||
|
||||
{ // this block will compile
|
||||
let a;
|
||||
a = 128;
|
||||
println!("{}", a); // no error here
|
||||
// variable 'a' has an initial value
|
||||
}
|
||||
```
|
||||
|
||||
If you declare a variable without an initial value and use it before assigning it some initial value, the Rust compiler will throw a **compile time error**.
|
||||
|
||||
Though errors are annoying. In this case, the Rust compiler is forcing you not to make one of the very common mistakes one makes when writing code: un-initialized variables.
|
||||
|
||||
### Rust compiler's error messages
|
||||
|
||||
Let's write a few programs where you
|
||||
|
||||
- Understand Rust's design by performing "normal" tasks, which are actually a major cause of memory-related issues
|
||||
- Read and understand the Rust compiler's error/warning messages
|
||||
|
||||
##### Testing variable immutability
|
||||
|
||||
Let us deliberately write a program that tries to modify a mutable variable and see what happens next.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let mut a = 172;
|
||||
let b = 273;
|
||||
println!("a: {a}, b: {b}");
|
||||
|
||||
a = 380;
|
||||
b = 420;
|
||||
println!("a: {}, b: {}", a, b);
|
||||
}
|
||||
```
|
||||
|
||||
Looks like a simple program so far until line 4. But on line 7, the variable `b`--an immutable variable--gets its value modified.
|
||||
|
||||
Notice the two methods of printing the values of variables in Rust. On line 4, I enclosed the variables between curly brackets so that their values will be printed. On line 8, I keep the brackets empty and provide the variables as arguments, C style. Both approaches are valid. (Except for modifying the immutable variable's value, everyting in this program is correct.)
|
||||
|
||||
Let's compile! You already know how to do that if you followed the previous chapter.
|
||||
|
||||
```
|
||||
$ rustc main.rs
|
||||
error[E0384]: cannot assign twice to immutable variable `b`
|
||||
--> main.rs:7:5
|
||||
|
|
||||
3 | let b = 273;
|
||||
| -
|
||||
| |
|
||||
| first assignment to `b`
|
||||
| help: consider making this binding mutable: `mut b`
|
||||
...
|
||||
7 | b = 420;
|
||||
| ^^^^^^^ cannot assign twice to immutable variable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0384`.
|
||||
```
|
||||
|
||||
> 📋 The word 'binding' refers to the variable name. This is an oversimplification, though.
|
||||
|
||||
This perfectly demonstrates Rust's robust error checking and informative error messages. The first line reads out the error message that prevents the compilation of the above code:
|
||||
|
||||
```
|
||||
error[E0384]: cannot assign twice to immutable variable b
|
||||
```
|
||||
|
||||
It means that the Rust compiler noticed that I was trying to re-assign a new value to the variable `b` but the variable `b` is an immutable variable. So that is causing this error.
|
||||
|
||||
The compiler even identifies the exact line and column numbers where this error is found.
|
||||
|
||||
Under the line that says `first assignment to `b`` is the line that provides help. Since I am mutating the value of the immutable variable `b`, I am told to declare the variable `b` as a mutable variable using the `mut` keyword.
|
||||
|
||||
> 🖥️ Implement a fix on your own to better understand the problem at hand.
|
||||
|
||||
##### Playing with uninitialized variables
|
||||
|
||||
Now, let's look at what the Rust compiler does when an uninitialized variable's value is read.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let a: i32;
|
||||
a = 123;
|
||||
println!("a: {a}");
|
||||
|
||||
let b: i32;
|
||||
println!("b: {b}");
|
||||
b = 123;
|
||||
}
|
||||
```
|
||||
|
||||
Here, I have two immutable variables `a` and `b` and both are uninitialized at the time of declaration. The variable `a` gets a value assigned before its value is read. But the variable `b`'s value is read before it is assigned an initial value.
|
||||
|
||||
Let's compile and see the result.
|
||||
|
||||
```
|
||||
$ rustc main.rs
|
||||
warning: value assigned to `b` is never read
|
||||
--> main.rs:8:5
|
||||
|
|
||||
8 | b = 123;
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
= note: `#[warn(unused_assignments)]` on by default
|
||||
|
||||
error[E0381]: used binding `b` is possibly-uninitialized
|
||||
--> main.rs:7:19
|
||||
|
|
||||
6 | let b: i32;
|
||||
| - binding declared here but left uninitialized
|
||||
7 | println!("b: {b}");
|
||||
| ^ `b` used here but it is possibly-uninitialized
|
||||
|
|
||||
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0381`.
|
||||
```
|
||||
|
||||
Here, the Rust compiler throws a compile time error and a warning. The warning says that the variable `b`'s value is never being read.
|
||||
|
||||
But that's preposterous! The value of variable `b` is being accessed on line 7. But look closely; the warning is regarding line 8. This is confusing; let's temporarily skip this warning and move on to the error.
|
||||
|
||||
The error message reads that `used binding `b` is possibly-uninitialized`. Like in the previous example, the Rust compiler is pointing out that the error is caused by reading the value of the variable `b` on line 7. The reason why reading the value of the variable `b` is an error is that its value is uninitialized. In the Rust programming language, that is illegal. Hence the compile time error.
|
||||
|
||||
> 🖥️ This error can be easily solved by swapping the codes of lines 7 and 8. Do it and see if the error goes away.
|
||||
|
||||
### Example program: Swap numbers
|
||||
|
||||
Now that you are familiar with the common variable-related issues, let's look at a program that swaps the values of two variables.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let mut a = 7186932;
|
||||
let mut b = 1276561;
|
||||
|
||||
println!("a: {a}, b: {b}");
|
||||
|
||||
// swap the values
|
||||
let temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
|
||||
println!("a: {}, b: {}", a, b);
|
||||
}
|
||||
```
|
||||
|
||||
Here, I have declared two variables, `a` and `b`. Both variables are mutable because I wish to change their values down the road. I assigned some random values. Initially, I print the values of these variables.
|
||||
|
||||
Then, on line 8, I create an immutable variable called `temp` and assign it the value stored in `a`. The reason why this variable is immutable is because `temp`'s value will not be changed.
|
||||
|
||||
To swap values, I assign the value of variable `b` to variable `a` and on the next line I assign the value of `temp` (which contains value of `a`) to variable `b`. Now that the values are swapped, I print values of variables `a` and `b`.
|
||||
|
||||
When the above code is compiled and executed, I get the following output:
|
||||
|
||||
```
|
||||
a: 7186932, b: 1276561
|
||||
a: 1276561, b: 7186932
|
||||
```
|
||||
|
||||
As you can see, the values are swapped. Perfect.
|
||||
|
||||
### Using Unused variables
|
||||
|
||||
When you have declared some variables you intend to use down the line but have not used them yet, and compile your Rust code to check something, the Rust compiler will warn you about it.
|
||||
|
||||
The reason for this is obvious. Variables that will not be used take up unnecessary initialization time (CPU cycle) and memory space. If it will not be used, why have it in your program in the first place? Though, the compiler does optimize this away. But it still remains an issue in terms of readability in form of excess code.
|
||||
|
||||
But sometimes, you might be in a situation where creating a variable might not be in your hands. Say when a function returns more than one value and you only need a few values. In that case, you can't tell the library maintainer to adjust their function according to your needs.
|
||||
|
||||
So, in times like that, you can have a variable that begins with an underscore and the Rust compiler will no longer give you such warnings. And if you really do not need to even use the value stored in said unused variable, you can simply name it `_` (underscore) and the Rust compiler will ignore it too!
|
||||
|
||||
The following program will not only not generate any output, but it will also not generate any warnings and/or error messages:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let _unnecessary_var = 0; // no warnings
|
||||
let _ = 0.0; // ignored completely
|
||||
}
|
||||
```
|
||||
|
||||
### Arithmetic operations
|
||||
|
||||
Since math is math, Rust doesn't innovate on it. You can use all of the arithmetic operators you might have used in other programming languages like C, C++ and/or Java.
|
||||
|
||||
A complete list of all the operations in the Rust programming language, along with their meaning, can be found [here][3].
|
||||
|
||||
#### Example Program: A Rusty thermometer
|
||||
|
||||
Following is a typical program that converts Fahrenheit to Celsius and vice a versa.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let boiling_water_f: f64 = 212.0;
|
||||
let frozen_water_c: f64 = 0.0;
|
||||
|
||||
let boiling_water_c = (boiling_water_f - 32.0) * (5.0 / 9.0);
|
||||
let frozen_water_f = (frozen_water_c * (9.0 / 5.0)) + 32.0;
|
||||
|
||||
println!(
|
||||
"Water starts boiling at {}°C (or {}°F).",
|
||||
boiling_water_c, boiling_water_f
|
||||
);
|
||||
println!(
|
||||
"Water starts freezing at {}°C (or {}°F).",
|
||||
frozen_water_c, frozen_water_f
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Not much is going on here... The Fahrenheit temperature is converted to Celsius and vice a versa for the temperature in Celsius.
|
||||
|
||||
As you can see here, since Rust does not allow automatic type casting, I had to introduce a decimal point to the whole numbers 32, 9 and 5. Other than that, this is similar to what you would do in C, C++ and/or Java.
|
||||
|
||||
As a learning exercise, try writing a program that finds out how many digits are in a given number.
|
||||
|
||||
### Constants
|
||||
|
||||
With some programming knowledge, you might know what this means. A constant is a special type of variable whose value **never changes**. _It stays constant_.
|
||||
|
||||
In the Rust programming language, a constant is declared using the following syntax:
|
||||
|
||||
```
|
||||
const CONSTANT_NAME: data_type = value;
|
||||
```
|
||||
|
||||
As you can see, the syntax to declare a constant is very similar to what we saw in declaring a variable in Rust. There are two differences though:
|
||||
|
||||
- A constant name should be in `SCREAMING_SNAKE_CASE`. All uppercase characters and words separated by an undercase.
|
||||
- Annotating the data type of the constant is **necessary**.
|
||||
|
||||
#### Variables vs Constants
|
||||
|
||||
You might be wondering, since the variables are immutable by default, why would the language also include constants?
|
||||
|
||||
The following table should help alleviate your doubts. (If you are curious and want to better understand these differences, you can look at [my blog][4] which shows these differences in detail.)
|
||||
|
||||
![A table that shows differences between Variables and Constants in the Rust programming language][5]
|
||||
|
||||
#### Example program using constants: Calculate area of circle
|
||||
|
||||
Following is a straightforward program about constants in Rust. It calculates the area and the perimeter of a circle.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
const PI: f64 = 3.14;
|
||||
let radius: f64 = 50.0;
|
||||
|
||||
let circle_area = PI * (radius * radius);
|
||||
let circle_perimeter = 2.0 * PI * radius;
|
||||
|
||||
println!("There is a circle with the radius of {radius} centimetres.");
|
||||
println!("Its area is {} centimetre square.", circle_area);
|
||||
println!(
|
||||
"And it has circumference of {} centimetres.",
|
||||
circle_perimeter
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
And upon running the code, the following output is produced:
|
||||
|
||||
```
|
||||
There is a circle with the radius of 50 centimetres.
|
||||
Its area is 7850 centimetre square.
|
||||
And it has circumference of 314 centimetres.
|
||||
```
|
||||
|
||||
### Variable shadowing in Rust
|
||||
|
||||
If you are a C++ programmer, you already sort of know what I am referring to. When the programmer **declares** a new variable with the same name as an already declared variable, it is known as variable shadowing.
|
||||
|
||||
Unlike C++, Rust allows you to perform variable shadowing in the same scope too!
|
||||
|
||||
> 💡 When a programmer shadows an existing variable, the new variable is assigned a new memory address but is referred with the same name as the existing variable.
|
||||
|
||||
Let us take a look at how it works in Rust.
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let a = 108;
|
||||
println!("addr of a: {:p}, value of a: {a}", &a);
|
||||
let a = 56;
|
||||
println!("addr of a: {:p}, value of a: {a} // post shadowing", &a);
|
||||
|
||||
let mut b = 82;
|
||||
println!("\naddr of b: {:p}, value of b: {b}", &b);
|
||||
let mut b = 120;
|
||||
println!("addr of b: {:p}, value of b: {b} // post shadowing", &b);
|
||||
|
||||
let mut c = 18;
|
||||
println!("\naddr of c: {:p}, value of c: {c}", &b);
|
||||
c = 29;
|
||||
println!("addr of c: {:p}, value of c: {c} // post shadowing", &b);
|
||||
}
|
||||
```
|
||||
|
||||
The `:p` inside curly brackets in the `println` statement is similar to using `%p` in C. It specifies that the value is in the format of a memory address (pointer).
|
||||
|
||||
I take 3 variables here. Variable `a` is immutable and is shadowed on line 4. Variable `b` is mutable and is also shadowed on line 9. Variable `c` is mutable but on line 14, only it's value is mutated. It is not shadowed.
|
||||
|
||||
Now, let's look at the output.
|
||||
|
||||
```
|
||||
addr of a: 0x7ffe954bf614, value of a: 108
|
||||
addr of a: 0x7ffe954bf674, value of a: 56 // post shadowing
|
||||
|
||||
addr of b: 0x7ffe954bf6d4, value of b: 82
|
||||
addr of b: 0x7ffe954bf734, value of b: 120 // post shadowing
|
||||
|
||||
addr of c: 0x7ffe954bf734, value of c: 18
|
||||
addr of c: 0x7ffe954bf734, value of c: 29 // post shadowing
|
||||
```
|
||||
|
||||
Looking at the output, you can see that not only the values of all three variables have changed, but the addresses of variables that were shadowed are are also different (check the last few hex characters).
|
||||
|
||||
The memory address for the variables `a` and `b` changed. This means that mutability, or lack thereof, of a variable is not a restriction when shadowing a variable.
|
||||
|
||||
### Conclusion
|
||||
|
||||
This article covers variables and constants in the Rust programming language. Arithmetic operations are also covered.
|
||||
|
||||
As a recap:
|
||||
|
||||
- Variables in Rust are immutable by default but mutability can be introduced.
|
||||
- Programmer needs to explicitly specify variable mutability.
|
||||
- Constants are always immutable no matter what and require type annotation.
|
||||
- Variable shadowing is declaring a _new_ variable with the same name as an existing variable.
|
||||
|
||||
Awesome! Good going with Rust I believe. In the next chapter, I'll discuss Data Types in Rust. Stay Tuned.
|
||||
|
||||
Meanwhile, if you have any questions, please let me know.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/rust-variables/
|
||||
|
||||
作者:[Pratham Patel][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[Cubik](https://github.com/Cubik65536)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/pratham/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[2]: https://itsfoss.com/rust-introduction/
|
||||
[3]: https://doc.rust-lang.org/book/appendix-02-operators.html?ref=itsfoss.com#operators
|
||||
[4]: https://blog.thefossguy.com/posts/immutable-vars-vs-constants-rs.md?ref=itsfoss.com
|
||||
[5]: https://itsfoss.com/content/images/2023/02/image.png
|
@ -0,0 +1,432 @@
|
||||
[#]: subject: "Rust Basics Series #2: Using Variables and Constants in Rust Programs"
|
||||
[#]: via: "https://itsfoss.com/rust-variables/"
|
||||
[#]: author: "Pratham Patel https://itsfoss.com/author/pratham/"
|
||||
[#]: collector: "lkxed"
|
||||
[#]: translator: "Cubik65536"
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
|
||||
Rust 基础系列 #2: 在 Rust 程序中使用变量和常量
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
在 [该系列的第一章](https://linux.cn/article-15709-1.html) 中,我讲述了为什么 Rust 是一门越来越流行的编程语言。我还展示了如何 [在 Rust 中编写 Hello World 程序](https://linux.cn/article-15709-1.html)。
|
||||
|
||||
让我们继续 Rust 之旅。在本文中,我将向您介绍 Rust 编程语言中的变量和常量。
|
||||
|
||||
此外,我还将讲解一个称为“遮蔽”的新编程概念。
|
||||
|
||||
### Rust 变量的独特之处
|
||||
|
||||
在 Rust 等编程语言中,变量是指 _存储某些数据的内存地址的别名_ 。
|
||||
|
||||
对 Rust 语言来讲也是如此。但是 Rust 有一个独特的“特性”。每个你声明的变量都是 **默认不可变的** 。这意味着一旦给变量赋值,就不能再改变它的值。
|
||||
|
||||
这个决定是为了确保默认情况下,你不需要使用 _自旋锁_ 或 _互斥锁_ 等特殊机制来引入多线程。Rust **会保证** 安全的并发。由于所有变量(默认情况下)都是不可变的,因此你不需要担心线程会无意中更改变量值。
|
||||
|
||||
这并不是在说 Rust 中的变量就像常量一样,因为它们确实不是。变量可以被显式地定义为可变的。这样的变量称为 **可变变量** 。
|
||||
|
||||
这是在 Rust 中声明变量的语法:
|
||||
|
||||
```
|
||||
// 默认情况下不可变
|
||||
// 初始化值是**唯一**的值
|
||||
let variable_name = value;
|
||||
|
||||
// 使用 'mut' 关键字定义可变变量
|
||||
// 初始化值可以被改变
|
||||
let mut variable_name = value;
|
||||
```
|
||||
|
||||
> 🚧 尽管你可以改变可变变量的值,但你不能将另一种数据类型的值赋值给它。
|
||||
|
||||
这意味着,如果你有一个可变的浮点型变量,你不能在后面将一个字符赋值给它。
|
||||
|
||||
### Rust 数据类型的高级概述
|
||||
|
||||
在上一篇文章中,你可能注意到了我提到 Rust 是一种强类型语言。但是在定义变量时,你不需要指定数据类型,而是使用一个通用的关键字 `let`。
|
||||
|
||||
Rust 编译器可以根据赋值给变量的值推断出变量的数据类型。但是如果你仍然希望明确指定数据类型并希望注释类型,那么可以这样做。以下是语法:
|
||||
|
||||
```
|
||||
let variable_name: data_type = value;
|
||||
```
|
||||
|
||||
接下来是 Rust 编程语言中一些常见的数据类型:
|
||||
|
||||
- **整数类型**:分别用于有符号和无符号的 32 位整数的 `i32` 和 `u32`
|
||||
- **浮点类型**:分别用于 32 位和 64 位浮点数的 `f32` 和 `f64`
|
||||
- **布尔类型**:`bool`
|
||||
- **字符类型**:`char`
|
||||
|
||||
我会在下一篇文章中更详细地介绍 Rust 的数据类型。现在,这应该足够了。
|
||||
|
||||
> 🚧 Rust 并不支持隐式类型转换。因此,如果你将值 `8` 赋给一个浮点型变量,你将会遇到编译时错误。你应该赋的值是 `8.` 或 `8.0`。
|
||||
|
||||
Rust 还强制要求在读取存储在其中的值之前初始化变量。
|
||||
|
||||
```
|
||||
{ // 该代码块不会被编译
|
||||
let a;
|
||||
println!("{}", a); // 本行报错
|
||||
// 读取一个**未初始化**变量的值是一个编译时错误
|
||||
}
|
||||
|
||||
{ // 该代码块会被编译
|
||||
let a;
|
||||
a = 128;
|
||||
println!("{}", a); // 本行不会报错
|
||||
// 变量 'a' 有一个初始值
|
||||
}
|
||||
```
|
||||
|
||||
如果你在不初始化的情况下声明一个变量,并在给它赋值之前使用它,Rust 编译器将会抛出一个 **编译时错误** 。
|
||||
|
||||
虽然错误很烦人,但在这种情况下,Rust 编译器强制你不要犯写代码时常见的错误之一:未初始化的变量。
|
||||
|
||||
### Rust 编译器的错误信息
|
||||
|
||||
来写几个程序,你将
|
||||
|
||||
- 通过执行“正常”的任务来理解 Rust 的设计,这些任务实际上是内存相关问题的主要原因
|
||||
- 阅读和理解 Rust 编译器的错误/警告信息
|
||||
|
||||
##### 测试变量的不可变性
|
||||
|
||||
让我们故意写一个试图修改不可变变量的程序,看看接下来会发生什么。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let mut a = 172;
|
||||
let b = 273;
|
||||
println!("a: {a}, b: {b}");
|
||||
|
||||
a = 380;
|
||||
b = 420;
|
||||
println!("a: {}, b: {}", a, b);
|
||||
}
|
||||
```
|
||||
|
||||
直到第 4 行看起来都是一个简单的程序。但是在第 7 行,变量 `b` —— 一个不可变变量 —— 的值被修改了。
|
||||
|
||||
注意打印 Rust 变量值的两种方法。在第 4 行,我将变量括在花括号中,以便打印它们的值。在第 8 行,我保持括号为空,并使用 C 的风格将变量作为参数。这两种方法都是有效的。(除了修改不可变变量的值,这个程序中的所有内容都是正确的。)
|
||||
|
||||
来编译一下!如果你按照上一章的步骤做了,你已经知道该怎么做了。
|
||||
|
||||
```
|
||||
$ rustc main.rs
|
||||
error[E0384]: cannot assign twice to immutable variable `b`
|
||||
--> main.rs:7:5
|
||||
|
|
||||
3 | let b = 273;
|
||||
| -
|
||||
| |
|
||||
| first assignment to `b`
|
||||
| help: consider making this binding mutable: `mut b`
|
||||
...
|
||||
7 | b = 420;
|
||||
| ^^^^^^^ cannot assign twice to immutable variable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0384`.
|
||||
```
|
||||
|
||||
> 📋 'binding' 一词是指变量名。但这只是一个简单的解释。
|
||||
|
||||
这很好的展示了 Rust 强大的错误检查和信息丰富的错误信息。第一行展示了阻止上述代码编译的错误信息:
|
||||
|
||||
```
|
||||
error[E0384]: cannot assign twice to immutable variable b
|
||||
```
|
||||
|
||||
这意味着,Rust 编译器注意到我试图给变量 `b` 重新赋值,但变量 `b` 是一个不可变变量。所以这就是导致这个错误的原因。
|
||||
|
||||
编译器甚至可以识别出错误发生的确切行和列号。
|
||||
|
||||
在显示 `first assignment to `b`` 的行下面,是提供帮助的行。因为我正在改变不可变变量 `b` 的值,所以我被告知使用 `mut` 关键字将变量 `b` 声明为可变变量。
|
||||
|
||||
> 🖥️ 自己实现一个修复来更好地理解手头的问题。
|
||||
|
||||
##### 使用未初始化的变量
|
||||
|
||||
现在,让我们看看当我们尝试读取未初始化变量的值时,Rust 编译器会做什么。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let a: i32;
|
||||
a = 123;
|
||||
println!("a: {a}");
|
||||
|
||||
let b: i32;
|
||||
println!("b: {b}");
|
||||
b = 123;
|
||||
}
|
||||
```
|
||||
|
||||
这里,我有两个不可变变量 `a` 和 `b`,在声明时都没有初始化。变量 `a` 在其值被读取之前被赋予了一个值。但是变量 `b` 的值在被赋予初始值之前被读取了。
|
||||
|
||||
来编译一下,看看结果。
|
||||
|
||||
```
|
||||
$ rustc main.rs
|
||||
warning: value assigned to `b` is never read
|
||||
--> main.rs:8:5
|
||||
|
|
||||
8 | b = 123;
|
||||
| ^
|
||||
|
|
||||
= help: maybe it is overwritten before being read?
|
||||
= note: `#[warn(unused_assignments)]` on by default
|
||||
|
||||
error[E0381]: used binding `b` is possibly-uninitialized
|
||||
--> main.rs:7:19
|
||||
|
|
||||
6 | let b: i32;
|
||||
| - binding declared here but left uninitialized
|
||||
7 | println!("b: {b}");
|
||||
| ^ `b` used here but it is possibly-uninitialized
|
||||
|
|
||||
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0381`.
|
||||
```
|
||||
|
||||
这里,Rust 编译器抛出了一个编译时错误和一个警告。警告说变量 `b` 的值从来没有被读取过。
|
||||
|
||||
但是这是荒谬的!变量 `b` 的值在第 7 行被访问了。但是仔细看;警告是关于第 8 行的。这很令人困惑;让我们暂时跳过这个警告,继续看错误。
|
||||
|
||||
这个错误信息说 `used binding `b` is possibly-uninitialized`。和之前的例子一样,Rust 编译器指出错误是由于尝试在第 7 行读取变量 `b` 的值而引起的。读取变量 `b` 的值是错误的原因是它的值没有初始化。在 Rust 编程语言中,这是非法的。因此编译时错误出现。
|
||||
|
||||
> 🖥️ 这个错误可以很容易地通过交换第 7 和第 8 行的代码来解决。试一下,看看错误是否消失了。
|
||||
|
||||
### 示例程序:交换数字
|
||||
|
||||
现在你已经熟悉了常见的变量相关问题,让我们来看一个交换两个变量值的程序。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let mut a = 7186932;
|
||||
let mut b = 1276561;
|
||||
|
||||
println!("a: {a}, b: {b}");
|
||||
|
||||
// 交换变量值
|
||||
let temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
|
||||
println!("a: {}, b: {}", a, b);
|
||||
}
|
||||
```
|
||||
|
||||
我在这里声明了两个变量 `a` 和 `b`。这两个变量都是可变的,因为我希望在后面改变它们的值。我赋予了一些随机值。最初,我打印了这些变量的值。
|
||||
|
||||
然后,在第 8 行,我创建了一个名为 `temp` 的不可变变量,并将存储在 `a` 中的值赋给它。之所以这个变量是不可变的,是因为 `temp` 的值不会改变。
|
||||
|
||||
要交换值,我将变量 `b` 的值赋给变量 `a`,在下一行,我将 `temp` 的值(它包含 `a` 的值)赋给变量 `b`。现在值已经交换了,我打印了变量 `a` 和 `b` 的值。
|
||||
|
||||
在编译并执行上面的代码后,我得到了以下输出:
|
||||
|
||||
```
|
||||
a: 7186932, b: 1276561
|
||||
a: 1276561, b: 7186932
|
||||
```
|
||||
|
||||
正如你所见,值已经交换了。完美。
|
||||
|
||||
### 使用未使用的变量
|
||||
|
||||
当你声明了一些变量,打算在后面使用它们,但是还没有使用它们,然后编译你的 Rust 代码来检查一些东西时,Rust 编译器会警告你。
|
||||
|
||||
原因是显而易见的。不会被使用的变量占用了不必要的初始化时间(CPU 周期)和内存空间。如果不会被使用,为什么要在程序写上它呢?尽管编译器确实会优化这一点。但是它仍然是一个问题,因为它会以多余的代码的形式影响可读性。
|
||||
|
||||
但是,有的时候,你可能会面对这样的情况:创建一个变量与否不在你的控制之下。比如说,当一个函数返回多个值,而你只需要其中的一些值时。在这种情况下,你不能要求库维护者根据你的需要调整他们的函数。
|
||||
|
||||
所以,在这种情况下,你可以写一个以下划线开头的变量,Rust 编译器将不再显示这样的警告。如果你真的不需要使用存储在该未使用变量中的值,你可以简单地将其命名为 `_`(下划线),Rust 编译器也会忽略它!
|
||||
|
||||
接下来的程序不仅不会生成任何输出,而且也不会生成任何警告和/或错误消息:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let _unnecessary_var = 0; // 没有警告
|
||||
let _ = 0.0; // 完全忽略
|
||||
}
|
||||
```
|
||||
|
||||
### 算术运算
|
||||
|
||||
数学就是数学,Rust 并没有在这方面创新。你可以使用在其他编程语言(如 C、C++ 和/或 Java)中使用过的所有算术运算符。
|
||||
|
||||
包含可以在 Rust 编程语言中使用的所有运算符和它们的含义的完整列表可以在 [这里][3] 找到。
|
||||
|
||||
#### 示例程序:一个生锈的温度计
|
||||
|
||||
> 译注:这里的温度计“生锈”了是因为它是使用 Rust(生锈)编写的,原作者在这里玩了一个双关。
|
||||
|
||||
接下来是一个典型的程序,它将华氏度转换为摄氏度,反之亦然。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let boiling_water_f: f64 = 212.0;
|
||||
let frozen_water_c: f64 = 0.0;
|
||||
|
||||
let boiling_water_c = (boiling_water_f - 32.0) * (5.0 / 9.0);
|
||||
let frozen_water_f = (frozen_water_c * (9.0 / 5.0)) + 32.0;
|
||||
|
||||
println!(
|
||||
"Water starts boiling at {}°C (or {}°F).",
|
||||
boiling_water_c, boiling_water_f
|
||||
);
|
||||
println!(
|
||||
"Water starts freezing at {}°C (or {}°F).",
|
||||
frozen_water_c, frozen_water_f
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
没什么大不了的... 华氏温度转换为摄氏温度,反之亦然。
|
||||
|
||||
正如你在这里看到的,由于 Rust 不允许自动类型转换,我不得不在整数 32、9 和 5 后放一个小数点。除此之外,这与你在 C、C++ 和/或 Java 中所做的类似。
|
||||
|
||||
作为练习,尝试编写一个程序,找出给定数中有多少位数字。
|
||||
|
||||
### 常量
|
||||
|
||||
如果你有一些编程知识,你可能知道这意味着什么。常量是一种特殊类型的变量,它的值**永远不会改变**。_它保持不变_。
|
||||
|
||||
在 Rust 编程语言中,使用以下语法声明常量:
|
||||
|
||||
```
|
||||
const CONSTANT_NAME: data_type = value;
|
||||
```
|
||||
|
||||
如你所见,声明常量的语法与我们在 Rust 中看到的变量声明非常相似。但是有两个不同之处:
|
||||
|
||||
- 常量的名字需要使用 `SCREAMING_SNAKE_CASE`。所有的大写字母和单词之间用下划线分隔。
|
||||
- 常量的数据类型**必须**被显性定义。
|
||||
|
||||
#### 变量与常量的对比
|
||||
|
||||
你可能在想,既然变量默认是不可变的,为什么语言还要包含常量呢?
|
||||
|
||||
接下来这个表格应该可以帮助你消除疑虑。(如果你好奇并且想更好地理解这些区别,你可以看看[我的博客][4],它详细地展示了这些区别。)
|
||||
|
||||
![一个展示 Rust 编程语言中变量和常量之间区别的表格][5]
|
||||
|
||||
#### 使用常量的示例程序:计算圆的面积
|
||||
|
||||
这是一个很直接的关于 Rust 中常量的简单程序。它计算圆的面积和周长。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
const PI: f64 = 3.14;
|
||||
let radius: f64 = 50.0;
|
||||
|
||||
let circle_area = PI * (radius * radius);
|
||||
let circle_perimeter = 2.0 * PI * radius;
|
||||
|
||||
println!("有一个周长为 {radius} 厘米的圆");
|
||||
println!("它的面积是 {} 平方厘米", circle_area);
|
||||
println!(
|
||||
"以及它的周长是 {} 厘米",
|
||||
circle_perimeter
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
如果运行代码,将产生以下输出:
|
||||
|
||||
```
|
||||
有一个周长为 50 厘米的圆
|
||||
它的面积是 7850 平方厘米
|
||||
以及它的周长是 314 厘米
|
||||
```
|
||||
|
||||
### Rust 中的变量遮蔽
|
||||
|
||||
如果你是一个 C++ 程序员,你可能已经知道我在说什么了。当程序员**声明**一个与已经声明的变量同名的新变量时,这就是变量遮蔽。
|
||||
|
||||
与 C++ 不同,Rust 允许你在同一作用域中执行变量遮蔽!
|
||||
|
||||
> 💡 当程序员遮蔽一个已经存在的变量时,新变量会被分配一个新的内存地址,但是使用与现有变量相同的名称引用。
|
||||
|
||||
来看看它在 Rust 中是如何工作的。
|
||||
|
||||
```
|
||||
fn main() {
|
||||
let a = 108;
|
||||
println!("a 的地址: {:p}, a 的值 {a}", &a);
|
||||
let a = 56;
|
||||
println!("a 的地址: {:p}, a 的值: {a} // 遮蔽后", &a);
|
||||
|
||||
let mut b = 82;
|
||||
println!("\nb 的地址: {:p}, b 的值: {b}", &b);
|
||||
let mut b = 120;
|
||||
println!("b的地址: {:p}, b的值: {b} // 遮蔽后", &b);
|
||||
|
||||
let mut c = 18;
|
||||
println!("\nc 的地址: {:p}, c的值: {c}", &c);
|
||||
c = 29;
|
||||
println!("c 的地址: {:p}, c的值: {c} // 遮蔽后", &c);
|
||||
}
|
||||
```
|
||||
|
||||
`println` 语句中花括号内的 `:p` 与 C 中的 `%p` 类似。它指定值的格式为内存地址(指针)。
|
||||
|
||||
我在这里使用了 3 个变量。变量 `a` 是不可变的,并且在第 4 行被遮蔽。变量 `b` 是可变的,并且在第 9 行也被遮蔽。变量 `c` 是可变的,但是在第 14 行,只有它的值被改变了。它没有被遮蔽。
|
||||
|
||||
现在,让我们看看输出。
|
||||
|
||||
```
|
||||
a 的地址: 0x7ffe954bf614, a 的值 108
|
||||
a 的地址: 0x7ffe954bf674, a 的值: 56 // 遮蔽后
|
||||
|
||||
b 的地址: 0x7ffe954bf6d4, b 的值: 82
|
||||
b 的地址: 0x7ffe954bf734, b 的值: 120 // 遮蔽后
|
||||
|
||||
c 的地址: 0x7ffe954bf734, c 的值: 18
|
||||
c 的地址: 0x7ffe954bf734, c 的值: 29 // 遮蔽后
|
||||
```
|
||||
|
||||
来看看输出,你会发现不仅所有三个变量的值都改变了,而且被遮蔽的变量的地址也不同(检查十六进制的最后几个字符)。
|
||||
|
||||
变量 `a` 和 `b` 的内存地址改变了。这意味着变量的可变性或不可变性并不是遮蔽变量的限制。
|
||||
|
||||
### 总结
|
||||
|
||||
本文介绍了 Rust 编程语言中的变量和常量。还介绍了算术运算。
|
||||
|
||||
做个总结:
|
||||
|
||||
- Rust 中的变量默认是不可变的,但是可以引入可变性。
|
||||
- 程序员需要显式地指定变量的可变性。
|
||||
- 常量总是不可变的,无论如何都需要类型注释。
|
||||
- 变量遮蔽是指使用与现有变量相同的名称声明一个 _新_ 变量。
|
||||
|
||||
很好!我相信和 Rust 一起的进展不错。在下一章中,我将讨论 Rust 中的数据类型。敬请关注。
|
||||
|
||||
与此同时,如果你有任何问题,请告诉我。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/rust-variables/
|
||||
|
||||
作者:[Pratham Patel][a]
|
||||
选题:[lkxed][b]
|
||||
译者:[Cubik](https://github.com/Cubik65536)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/pratham/
|
||||
[b]: https://github.com/lkxed/
|
||||
[1]: https://itsfoss.com/content/images/2023/03/linux-mega-packt.webp
|
||||
[2]: https://itsfoss.com/rust-introduction/
|
||||
[3]: https://doc.rust-lang.org/book/appendix-02-operators.html?ref=itsfoss.com#operators
|
||||
[4]: https://blog.thefossguy.com/posts/immutable-vars-vs-constants-rs.md?ref=itsfoss.com
|
||||
[5]: https://itsfoss.com/content/images/2023/02/image.png
|
Loading…
Reference in New Issue
Block a user