Skip to content

变量

变量

变量默认不可变,添加 mut 使其可变。

rust
let x = 1;
let mut y = 123;

常量

常量永远不可变。声明时要用 const 关键字,并必须标注类型。

常量命名约定是全大写。作用域内永远可用。

习惯将硬编码提取为常量。

rust
const LIGHT_SPEED = 300000000;

遮蔽

可以重复使用同一个变量名来遮蔽一个变量,遮蔽的持续时间是:1. 再次被遮蔽; 2. 作用域结束。

mut 的区别:

  1. 不用 mut 而修改变量会报错,而用 let 遮蔽变量则可以对值做一些变换,变换完成后还能回到之前的值。
rust
fn main() {
    let x = 5;
    let x = x + 1;
    {
        let x = x * 2;
        println!("The value of x in the inner scope is {x}");
    }

    println!("The value of x is {x}");
}
shell
$ cargo run 

x in scope: 12
outer x : 6
  1. 遮蔽实际上是创建新的变量,所以可以改变值的类型。
rust
let spaces = "   ";
let spaces = spaces.len();

类型

Rust 是静态类型,编译时就必须知道所有的类型。

整形

长度有符号无符号
8-biti8u8
16-biti16u16
32-biti32u32
64-biti64u64
128-biti128u128
架构相关isizeusize

有符号的可以表示 -2(^(n-1)) 到 2^(n-1) - 1 之间的数字,无符号的可以表示 0 到 2^n - 1 之间的数字。u8 能存 0 到 2^8 - 1 = 255。

可以使用字面值来表示整数

数字字面值例子
Decimal(十进制)98_222
Hex(十六进制)0xff
Octal(八进制)0o77
Binary(二进制)0b1111_0000
Byte(字节字面值,仅限 u8)b'A'

也可以显示指定类型:57u8。数字字面值可以使用 _ 作为视觉分隔符:1_0001000 完全相同。

浮点型

f32f64,默认是 f64,浮点型都是有符号的。

数值运算

加减乘除取余。

整数除法会向零舍入到最接近的整数。

rust
let res = 56.7 / 32.2
// => -1

布尔类型

truefalse

字符

char,使用单引号表示一个字符,占 4 个字节。

rust
let z: char = 'Z';

复合类型:元组和数组

元组

元组每个位置都可以是一个类型,每个位置的类型不必相同。

rust
let tup: (i32, f64, u8) = (500, 6.4, 1);

元组可以解构

rust
let (x, y, z) = tup;

也可以用点 . 和索引直接访问。

rust
let x = tup.0;

没有任何元素的元组叫 单元,写作 ()

数组

Rust 的数组长度是固定的。

数组会在栈上分配一整块、大小已知且固定的内存。

数组的定义方式:

rust
let arr: [i32; 5] = [1, 2, 3, 4, 5];

当数组元素全都一样时,这样写更简洁:

rust
let arr = [3; 5];
// 等价于
let arr = [3, 3, 3, 3, 3];

函数

fn 开头,() 参数,{} 函数体。

Rust 不关心函数的位置,只要调用时在可见作用域内就行。

参数

语句和表达式

语句是执行一些操作但不返回值,表达式计算并产生一个值。

函数定义是语句,函数调用是表达式。

语句不返回值,所以

rust
fn main() {
    let x = (let y = 6);
}

会报错。

下面代码:

rust
fn main() {
    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {y}");
}

中,表达式:

rust
{
    let x = 3;
    x + 1
}

是一个代码块。

表达式的结尾没有分号,表达式加分号就变成了语句,而语句不会返回值。

x = 3 中,3 是一个表达式,赋值给了 x。而 let x = 3; 是一条语句,不返回值。

具有返回值的函数

函数会把返回值返回给调用它的代码,必须在箭头(->)后面声明返回值类型。函数的返回值是函数体中最后一个表达式的值,或者用 return 指定一个值。

rust
fn five() -> i32 {
    5
}

fn main() {
    let x = five();
    println!("five is : {x}");
}

控制流

if

rust
if condition {} 
else {}

if 是表达式,if 的 {} 中的代码块也是表达式,所以 if 可以用来赋值。

rust
let x = if input < 10 { "small" } else { "big" };

循环

loop, while, for

循环还能用来返回值(Rust 真几把牛逼)。

rust
let mut counter = 2;
    let x = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2
        }
    };

println!("x is {x}");

循环标签

可以使用循环标签来标注具体某个循环,告诉 break 和 continue 要处理哪个循环。

rust
fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}

while 和 for

rust
while condition {

}
rust
for number in (1..5) {

}
rust
let elements = [1, 4, 6, 7, 8];
for item in elements {
    println!("curr item is {item}");
}

所有权

字符串会在栈上保存内容指针、长度、容量,而内容会保存在堆上。

当把字符串赋值给另一个变量时,会创建一个拷贝,把栈上的数据拷贝过去,此时两个指针指向堆上的同一个内容。

赋值后,先前的值将不再有效。

rust
let s1 = String::from("hello");
let s2 = s1;
println!("{s1}{s2}");

这个会出现报错:

sh
$ cargo run
   Compiling ownership v0.1.0 (/Volumes/workspace/rustlearn/ownership)
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:20
  |
3 |         let s1 = String::from("hello");
  |             -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
4 |         let s2 = s1;
  |                  -- value moved here
5 |         println!("{s1}{s2}");
  |                    ^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
4 |         let s2 = s1.clone();
  |                    ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error                       exit:101

这个过程如下图所示

alt text

如果需要复制堆上的数据,可以使用 clone。下面的代码就可以运行了。

rust
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1}{s2}");

像整形这种存储在栈上的类型,它们实现了 Copy trait,不会在复制为 y 之后使 x 失效。

rust
let x = 5;
let y = x;
println!("{x}, {y}");

任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:

  • 所有整数类型,比如 u32。
  • 布尔类型,bool,它的值是 true 和 false。
  • 所有浮点数类型,比如 f64。
  • 字符类型,char。
  • 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。

将值传递给函数时也符合这种原理,例如:

rust
fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    println!("{s}");

    let x = 5;
    makes_copy(x);
    println!("{x}")
}

fn takes_ownership(some_string: String) {
    println!("{some_string}");
}

fn makes_copy(some_int: i32) {
    println!("{some_int}");
}

这会出现报错:

sh
cargo run
   Compiling ownership v0.1.0 (/Volumes/workspace/rustlearn/ownership)
error[E0382]: borrow of moved value: `s`
  --> src/main.rs:13:20
   |
11 |         let s = String::from("hello");
   |             - move occurs because `s` has type `String`, which does not implement the `Copy` trait
12 |         takes_ownership(s);
   |                         - value moved here
13 |         println!("{s}");
   |                    ^ value borrowed here after move
   |
note: consider changing this parameter type in function `takes_ownership` to borrow instead if owning the value isn't necessary
  --> src/main.rs:21:33
   |
21 | fn takes_ownership(some_string: String) {
   |    ---------------              ^^^^^^ this parameter takes ownership of the value
   |    |
   |    in this function
help: consider cloning the value if the performance cost is acceptable
   |
12 |         takes_ownership(s.clone());
   |                          ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error     exit:101

因为在 takes_ownership 结束后,some_string 移出作用域并且被 drop 了。

返回值也会如此。

rust
fn main() {
    let s1 = gives_ownership();        // gives_ownership 将它的返回值传递给 s1

    let s2 = String::from("hello");    // s2 进入作用域

    let s3 = takes_and_gives_back(s2); // s2 被传入 takes_and_gives_back, 
                                       // 它的返回值又传递给 s3
} // 此处,s3 移出作用域并被丢弃。s2 被 move,所以无事发生
  // s1 移出作用域并被丢弃

fn gives_ownership() -> String {       // gives_ownership 将会把返回值传入
                                       // 调用它的函数

    let some_string = String::from("yours"); // some_string 进入作用域

    some_string                        // 返回 some_string 并将其移至调用函数
}

// 该函数将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String {
    // a_string 进入作用域

    a_string  // 返回 a_string 并移出给调用的函数
}

最后更新于:

评论区
评论区空空如也
发送评论
名字
0 / 20
邮箱
0 / 100
评论内容
0 / 140
由于是非实名评论,所以不提供删除功能。如果你需要删除你发送的评论,或者是其他人的评论对你造成了困扰,请 发邮件给我 。同时评论区会使用 AI + 人工的方式进行审核,以达到合规要求。