文章目录

Rust 基本语法:变量、函数、所有权

发布于 2026-04-04 11:48:31 · 浏览 18 次 · 评论 0 条

Rust 基本语法:变量、函数、所有权

Rust 是一门注重安全与性能的现代系统编程语言。它的语法简洁独特,尤其"所有权"机制是其核心特性。本文将快速掌握 Rust 的三大基础概念。


1. 变量与不可变性

1.1 变量声明

使用 let 关键字声明变量。默认情况下,Rust 变量是不可变的——这正是 Rust 安全性的基石。

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
}

尝试修改 x 的值会导致编译错误:

fn main() {
    let x = 5;
    x = 6; // 错误:无法修改不可变变量
}

1.2 可变变量

若需要修改变量值,使用 mut 关键字。

fn main() {
    let mut x = 5;
    println!("Before: {}", x);
    x = 6;
    println!("After: {}", x);
}

输出结果为 Before: 5After: 6

1.3 变量隐藏

可以在同一个作用域内重新声明同名变量,这称为"隐藏"(Shadowing)。

fn main() {
    let x = 5;
    let x = x + 1;  // 隐藏:创建新变量 x
    let x = x * 2;  // 再次隐藏
    println!("The value of x is: {}", x); // 输出 12
}

隐藏与 mut 的区别:隐藏可以改变类型,而 mut 不能。

fn main() {
    let spaces = "   ";           // 字符串类型
    let spaces = spaces.len();    // 转换为数字类型
    println!("{}", spaces);       // 输出 3
}

1.4 常量

常量使用 const 声明,必须标注类型,且值不可改变。

const MAX_POINTS: u32 = 100_000;

2. 函数

2.1 函数定义

使用 fn 关键字定义函数。函数名采用 snake_case 风格(小写下划线分隔)。

fn main() {
    println!("Hello, world!");
    another_function();
}

fn another_function() {
    println!("Another function.");
}

2.2 函数参数

函数参数必须声明类型。

fn main() {
    print_labeled_measurement(5, 'h');
}

fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {}{}", value, unit_label);
}

调用 print_labeled_measurement(5, 'h') 输出 The measurement is: 5h

2.3 函数返回值

Rust 函数返回值类型用 -> 符号声明。最后一个表达式的值即为返回值(无需 return 关键字)。

fn five() -> i32 {
    5
}

fn main() {
    let x = five();
    println!("The value of x is: {}", x); // 输出 5
}

使用 return 可提前返回:

fn main() {
    let result = plus_one(5);
    println!("Result: {}", result); // 输出 6
}

fn plus_one(x: i32) -> i32 {
    return x + 1;
}

2.4 语句与表达式

Rust 代码由语句和表达式组成。语句执行操作但不返回值,表达式计算并产生值。

fn main() {
    let y = {          // 语句块
        let x = 3;
        x + 1          // 表达式,无分号
    };                 // 语句结束需分号
    println!("The value of y is: {}", y); // 输出 4
}

注意:表达式结尾无分号,分号使其变为语句(返回值单元类型 ())。


3. 所有权

所有权是 Rust 最独特的特性,它使 Rust 无需垃圾回收器即可保证内存安全。

3.1 所有权规则

Rust 所有权遵循三条核心规则:

  1. Rust 中的每个值都有一个变量作为其所有者。
  2. 同一时间只能有一个所有者。
  3. 当所有者离开作用域时,值会被丢弃。
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权移动到 s2

    // println!("{}, world!", s1); // 错误:s1 不再有效
    println!("{}, world!", s2);   // 正确:s2 拥有值
}

以上代码中,s1 的所有权转移给 s2,此后 s1 失效。这称为"移动"(Move)。

3.2 克隆与 Copy

若需要深拷贝,使用 clone() 方法(仅支持实现 Clone trait 的类型)。

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝

    println!("s1 = {}, s2 = {}", s1, s2); // 两者都有效
}

对于标量类型(如整数、布尔、字符等),它们实现了 Copy trait,赋值时会自动拷贝。

fn main() {
    let x = 5;
    let y = x;  // x 和 y 都有效

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

以下类型可自动 Copy:所有整数类型、布尔类型、浮点类型、字符类型、元组(仅当元素都 Copy 时)。

3.3 函数中的所有权

将值传递给函数时,所有权同样会转移。

fn main() {
    let s = String::from("hello");
    takes_ownership(s);  // s 的所有权转移

    // println!("{}", s); // 错误:s 已失效

    let x = 5;
    makes_copy(x);       // x 仍有效(i32 是 Copy)
    println!("x = {}", x);
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string); // some_string 离开作用域后被丢弃
}

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer); // some_integer 离开作用域后无影响
}

3.4 引用与借用

若不想转移所有权,可使用"引用"(Reference)。借用(Borrowing)指通过引用使用值而不获得所有权。

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // 借用 s1

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {  // s 是引用
    s.len()
} // s 离开作用域,但不丢弃值

3.5 可变引用

默认引用不可修改。可变引用需要 &mut 声明,但作用域内同一时间只能有一个可变引用。

fn main() {
    let mut s = String::from("hello");
    change(&mut s);  // 可变引用

    println!("{}", s); // 输出 "hello, world!"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world!");
}

以下代码存在编译错误(防止数据竞争):

let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // 错误:同一作用域内只能有一个可变引用

3.6 切片

切片(Slice)允许引用集合中的连续元素序列,而不获取所有权。

字符串切片

fn main() {
    let s = String::from("hello world");
    let hello = &s[0..5];      // 指向 "hello"
    let world = &s[6..11];     // 指向 "world"

    println!("{}", hello); // 输出 hello
    println!("{}", world); // 输出 world
}

语法糖:[0..5] 等同于 [..5][6..11] 等同于 [6..],完整字符串是 [..]

函数改进

使用切片作为参数,使函数更通用。

fn first_word(s: &str) -> &str {  // 接受字符串切片
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }
    &s[..]
}

此函数可接受 String&str 类型。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文