Table of contents
1. Variability of variables
Variables are immutable by default. This is one of the many advantages that Rust gives you, allowing you to write code that takes full advantage of the safety and simple concurrency that Rust provides. However, you can still use mutable variables.
Next, let’s test it. Use the cargo new variables command to generate a new project called variables in the projects directory.
Modify main.rs as follows:
fn main() {
let v = 1;
v = 2;
println!("Hello, world! {v}");
}
Opening it in VSCode, we found that the rust-analyzer plug-in prompted that immutable variables could not be modified.
But variability is also very useful and can be used to write code more easily. Although variables are immutable by default, you can still make them mutable by adding mut
in front of the variable name. The code changes to the following:
fn main() {
let mut v = 1;
println!("Hello, world! {v}");
v = 2;
println!("Hello, world! {v}");
}
constant
Similar to immutable variables,constants are values bound to a name that are not allowed to change, but constants and variables There are still some differences.
First, the use of mut
with constants is not allowed. Constants are not only immutable by default, they are always immutable. To declare a constant, use the const
keyword instead of let
, and must declare the type of the value .
Change the above code to look like this:
fn main() {
const PI: f64 = 3.14159;
println!("Hello, world! {PI}");
}
Hide
In the same scope, you can declare a variable multiple times, use the same variable name to hide a variable, and reuse the let
keyword to hide it multiple times, as shown below:
fn main() {
let name = "zhangsan";
let name = "lisi";
println!("我的名字是:{name}");
{
let name = "wangwu";
let name = "zhaoliu";
println!("我的名字是:{name}");
}
}
The corresponding result will be printed, but there will be a prompt where the definition is repeated, saying that the variable is not referenced. If it is intentional, it is recommended to add an underscore prefix.
There is a difference between hiding and marking a variable as mut
. When you accidentally try to reassign a variable without using the let
keyword, it will cause a compile-time error. By using let
, we can do some calculations with this value, but the variable will still be immutable after the calculation.
mut
Another difference between and hiding is that when let
is used again, a new variable is actually created. We can change the type of the value and reuse the name. For example, suppose the program asks the user to enter a space character to indicate how many spaces they want to appear between text, and next we want to store the input as a number (how many spaces):
fn main() {
let spaces = "zhangsan";
let spaces = spaces.len();
println!("长度是:{spaces}")
}
In editing, you can see that the same variable is assigned a value and the corresponding type has also changed.
The first spaces
variable is of string type, and the second spaces
variable is of numeric type. Hiding saves us from having to use different names. What happens if we declare a mutable variable via mut?
fn main() {
let mut spaces = "zhangsan";
spaces = spaces.len();
println!("长度是:{spaces}");
}
This shows that at the beginning, the type of the variable we declared was fixed, and we cannot change the type of the variable.
2. Data type
Each value belongs to a certain data type (data type), this tells Rust what kind of data it is designated as, so that it is clear how the data is handled. We will see two subsets of data types: scalar and compound
When uses parse
to convert String
into a number, a type annotation must be added, like this:
let guess:u32 = "100".parse().expect("");
If no type definition is added to guess, the following error will occur:
2.1 Scalar types
The scalar (scalar) type represents a single value. Rust has four basic scalar types: integers, floats, booleans, and characters.
integer
Integer is a number without a decimal part. The u32
integer type was used in the previous example. This type declaration indicates that the value associated with it should be an unsigned integer occupying 32 bits (signed integer types begin with i
instead of u
), as follows The table shows Rust's built-in integer types.
length | Signed | unsigned |
---|---|---|
8-bit | i8 |
u8 |
16-bit | i16 |
u16 |
32-bit | i32 |
u32 |
64-bit | i64 |
u64 |
128-bit | i128 |
u128 |
arch | isize |
usize |
isize
The and usize
types depend on the computer architecture on which the program is running: they are 64-bit on a 64-bit architecture, and 32-bit on a 32-bit architecture.
Note: Numeric literals that can be of multiple numeric types allow the use of type suffixes, such as
57u8
to specify the type, and also allow the use of < /span> you specified. , its value is the same as the_
is used as a delimiter to facilitate reading, for example,1_000
1000
numeric literal | example |
---|---|
Decimal (decimal) | 98_222 |
Hex (hexadecimal) | 0xff |
Octal (octal) | 0o77 |
Binary | 0b1111_0000 |
Byte (single-byte character) (u8 only) |
b'A' |
floating point
Rust also has two native floating-point numbers (floating-point numbersf32
< a i=4>) type, they are numbers with a decimal point. Rust's floating point types are and f64
, which occupy 32 and 64 bits respectively. The default type is f64
because on modern CPUs it is almost as fast as f32
but with more precision. All floating point types are signed.
Here is an example showing floating point numbers:
Floating point numbers are represented by the IEEE-754 standard. f32
is a single-precision floating-point number, and f64
is a double-precision floating-point number.
Numerical operations
All numeric types in Rust support basic mathematical operations: addition, subtraction, multiplication, division, and remainder. Integer division rounds toward zero to the nearest integer. Here are some examples:
fn main() {
let plus = 2 + 3;
let sub = 5 - 2;
let mul = 2 * 3;
let div = 3 /2;
let rem = 3 % 2;
println!("{plus} - {sub} - {mul} - {div} - {rem}")
}
boolean
The Boolean type in Rust has two possible values: true
and false
. Boolean types in Rust are represented using bool
. For example:
fn main() {
let a = false;
let b:bool = true;
print!("{a} {b}")
}
Character type
Rust’s char
type is the most native alphabetic type in the language. Here are some examples of declaring char
values:
fn main() {
let a = 'A';
let b:char = 'a';
println!("{a} - {b}")
}
composite type
Composite types can combine multiple values into a single type. Rust has two native composite types: tuples and arrays.
tuple type
Tuples are a primary way to combine values from multiple other types into a composite type. A tuple has a fixed length: once declared, its length does not grow or shrink.
We create a tuple using a comma-separated list of values enclosed in parentheses. Each position in the tuple has a type, and the types of the different values do not have to be the same. This example uses optional type annotations:
let tup: (char, f32, bool) = ('A', 3.2, true);
tup
The variable is bound to the entire tuple because the tuple is a single composite element. To get a single value from a tuple, you can use pattern matching to destructure the tuple value, like this:
fn main() {
let tup: (char, f32, bool) = ('A', 3.2, true);
let (x,y,z) = tup;
println!("{x} {y} {z}") // A 3.2 true
}
array type
Another way to contain multiple values is array (array). Unlike tuples, each element in an array must be of the same type. Arrays in Rust differ from arrays in some other languages in that arrays in Rust have a fixed length.
fn main() {
let x = [1, 2, 3, 4, 5];
}
You can also create an array with the same value for each element by specifying the initial value in square brackets followed by a semicolon and then the number of elements:
fn main() {
let a = [3; 5];
}
The array named a will contain 5 elements, and the values of these elements will initially be set to 3. This way of writing has the same effect as let a = [3,3,3,3,3].
Access array elements
fn main() {
let x = [1, 2, 3, 4, 5];
let y = x[0];
let z = x[9];
println!("{z}")
}
We can get the value through the index of the array, just like getting the value in js and ts languages.
When accessing an index that exceeds the bounds, a runtime error will be reported.
The program caused a runtime error when it used an invalid value in an indexing operation. The program exits with an error message and does not execute the final println!
statement. When trying to access an element using an index, Rust checks whether the specified index is less than the length of the array. If the index exceeds the length of the array, Rust panics , which is Rust terminology for when a program exits with an error. This check must be done at runtime, especially in this case, because the compiler has no way of knowing what values the user will enter when the code is later run.