10分钟Solana-性能web3-2.2 Rust 编程基础一:环境搭建、基础语法与所有权

1 阅读7分钟

欢迎订阅专栏10分钟Solana-性能web3

Rust 编程基础一:环境搭建、基础语法与所有权

本系列教程面向有编程经验(如 JavaScript、Python 或 Solidity)的开发者,通过对比和实例,帮助你快速掌握 Rust 的核心概念。本文是系列第一篇,涵盖环境搭建、基础语法和所有权模型。

一、为什么学习 Rust?

Rust 是一门系统级编程语言,以其内存安全高性能并发性著称。在区块链领域,它是 SolanaPolkadotNear 等公链的核心开发语言。

核心优势

  • 无需 GC(垃圾回收):在编译时通过所有权系统保证内存安全。
  • 零成本抽象:高级语言特性编译后与手写底层代码一样高效。
  • 可靠的并发:所有权系统天然防止数据竞争。

如果你有 Solidity 经验,会发现 Rust 在安全性上的追求与智能合约安全审计的理念高度一致。


二、环境搭建

1. 安装 Rust

推荐使用 rustup 安装工具链:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装完成后,重新打开终端或执行:

source ~/.cargo/env

验证安装:

rustc --version   # 编译器版本
cargo --version   # 包管理工具版本

2. 创建第一个项目

使用 Cargo(Rust 的包管理工具)创建项目:

cargo new hello_rust
cd hello_rust

目录结构如下:

hello_rust/
├── Cargo.toml      # 项目配置(类似 package.json)
└── src/
    └── main.rs     # 主入口文件

Cargo.toml 内容:

[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"

[dependencies]

3. 运行程序

src/main.rs 默认内容:

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

运行:

cargo run

你将看到输出:Hello, world!

常用 Cargo 命令

命令用途
cargo new <name>创建新项目
cargo build编译项目(调试模式)
cargo build --release编译项目(发布模式,优化)
cargo run编译并运行
cargo test运行测试
cargo check检查代码但不编译(快速)

三、变量与数据类型

1. 变量声明与不可变性

Rust 中的变量默认不可变。这是安全性的重要设计。

fn main() {
    let x = 5;
    // x = 6; // 编译错误:不能对不可变变量赋值

    let mut y = 10; // 使用 mut 声明可变
    y = 20; // 允许
    println!("x = {}, y = {}", x, y);
}

与 Solidity 对比

  • Solidity:默认 storage 变量可修改,constant 不可改。
  • Rust:默认不可变,需显式 mut 才可变。

2. 常量与变量

  • 常量:编译时确定,全局有效,使用 const 声明。
  • 变量:运行时确定,作用域内有效。
const MAX_POINTS: u32 = 100_000; // 常量,必须标注类型

fn main() {
    let x = 5; // 变量
}

3. 数据类型

Rust 是静态强类型语言,但支持类型推断

标量类型
分类类型说明
整数i8, i16, i32, i64, i128有符号整数
u8, u16, u32, u64, u128无符号整数
isize, usize与平台相关(指针宽度)
浮点数f32, f64默认 f64
布尔booltrue / false
字符char4 字节 Unicode,用单引号
fn main() {
    let a: u32 = 42;
    let b = 3.14; // 默认 f64
    let c = true;
    let d = '🦀'; // char 可以存储 emoji
}
复合类型

元组 (Tuple):固定长度,可包含不同类型。

let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup; // 解构
println!("x = {}, y = {}", x, y);

let first = tup.0; // 索引访问

数组 (Array):固定长度,所有元素类型相同。

let arr = [1, 2, 3, 4, 5]; // 类型 [i32; 5]
let first = arr[0];

4. 类型转换

Rust 不支持隐式类型转换,必须显式转换。

let a: u32 = 42;
let b: u64 = a as u64; // 显式转换

四、控制流

1. 条件表达式 if

Rust 的 if表达式(有返回值),且条件必须是 bool 类型。

fn main() {
    let number = 7;

    if number < 5 {
        println!("小于 5");
    } else if number == 5 {
        println!("等于 5");
    } else {
        println!("大于 5");
    }

    // 作为表达式使用
    let is_even = if number % 2 == 0 { "偶数" } else { "奇数" };
    println!("{}", is_even);
}

2. 循环

loop:无限循环
let mut count = 0;
loop {
    count += 1;
    if count == 3 {
        break; // 跳出循环
    }
}
while:条件循环
let mut n = 3;
while n > 0 {
    println!("{}", n);
    n -= 1;
}
for:遍历集合(推荐)
let arr = [10, 20, 30, 40];

for element in arr.iter() {
    println!("{}", element);
}

// 范围遍历
for i in 0..5 { // 0, 1, 2, 3, 4
    println!("{}", i);
}

for i in 0..=5 { // 0, 1, 2, 3, 4, 5(包含右边界)
    println!("{}", i);
}

五、所有权系统

所有权是 Rust 最独特的核心概念,也是新手最容易困惑的地方。理解它,就理解了 Rust 的内存安全基础。

1. 所有权规则

  1. 每个值有一个所有者(变量)。
  2. 值在任一时刻只能有一个所有者。
  3. 当所有者离开作用域,值被释放(自动调用 drop)。

2. 作用域

fn main() {
    {                      // s 在此处未定义
        let s = "hello";   // s 进入作用域
        // s 在此处有效
    }                      // s 离开作用域,内存被释放
    // s 在此处无效
}

3. String 类型与内存管理

Rust 需要管理存储在上的数据。String 是一个堆分配的字符串类型。

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权被移动到 s2

    // println!("{}", s1); // 编译错误:s1 已失效
    println!("{}", s2);    // 正确
}

为什么 s1 失效了?

  • s1 赋值给 s2 时,Rust 不会复制堆上的数据(成本高),而是**移动(Move)**所有权。
  • 这避免了双重释放(Double Free)的内存安全问题。

如果确实需要复制

let s1 = String::from("hello");
let s2 = s1.clone(); // 显式深拷贝
println!("{} {}", s1, s2); // 都有效

与 Solidity 对比

  • Solidity 中 storage 变量赋值可能产生引用,但 memory 变量赋值会复制。
  • Rust 的所有权规则更严格,在编译时就防止了悬垂引用。

4. 引用与借用

使用 & 创建引用,允许不获取所有权即可访问值。

fn main() {
    let s = String::from("hello");
    let len = calculate_length(&s); // 传入引用
    println!("'{}' 的长度是 {}", s, len); // s 仍然有效
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

规则

  • 同一时刻,要么有多个不可变引用&T),要么有一个可变引用&mut T)。
  • 引用必须始终有效(编译器检查生命周期)。
fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // 不可变引用
    let r2 = &s; // 可以有多个
    // let r3 = &mut s; // 编译错误:不能同时有可变和不可变引用

    println!("{} {}", r1, r2);
}

fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s; // 可变引用
    // let r2 = &mut s; // 编译错误:不能同时有多个可变引用
    r1.push_str(", world"); // 通过可变引用修改
    println!("{}", r1);
}

5. 切片 (Slice)

切片是集合的局部引用,没有所有权

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

    // 切片是 &str 类型
    let full: &str = &s[..]; // 全部
}

六、综合示例:与 Solidity 的对比

以下是一个简单的 Rust 函数,模拟 Solidity 中的余额映射:

use std::collections::HashMap;

struct Bank {
    balances: HashMap<String, u64>,
}

impl Bank {
    // 存款:修改余额
    fn deposit(&mut self, account: String, amount: u64) {
        let balance = self.balances.entry(account).or_insert(0);
        *balance += amount;
    }

    // 取款:检查余额并扣减
    fn withdraw(&mut self, account: &str, amount: u64) -> Result<(), String> {
        if let Some(balance) = self.balances.get_mut(account) {
            if *balance >= amount {
                *balance -= amount;
                Ok(())
            } else {
                Err("余额不足".to_string())
            }
        } else {
            Err("账户不存在".to_string())
        }
    }

    // 查询余额
    fn balance(&self, account: &str) -> u64 {
        self.balances.get(account).copied().unwrap_or(0)
    }
}

fn main() {
    let mut bank = Bank {
        balances: HashMap::new(),
    };

    bank.deposit("alice".to_string(), 100);
    bank.deposit("alice".to_string(), 50);

    match bank.withdraw("alice", 30) {
        Ok(()) => println!("提款成功,余额: {}", bank.balance("alice")),
        Err(e) => println!("提款失败: {}", e),
    }
}

与 Solidity 的关键区别

  • Rust 使用 HashMap 模拟映射,需要显式管理键值。
  • Rust 通过 Result 类型显式处理错误,而非 revert
  • Rust 没有 address 原生类型,使用 StringPubkey(Solana 中)。

七、总结与下一步

本文覆盖了 Rust 的基础语法和所有权系统,这些是理解后续高级特性(生命周期、Trait、闭包、并发)的前提。

关键要点回顾:

  • 变量默认不可变,需 mut 声明可变。
  • 所有权规则决定了值何时被释放,移动 vs 克隆
  • 引用(&)允许借用值而不转移所有权,但受严格规则约束。
  • 切片(&str)是常用的无所有权字符串引用。

下一步学习建议:

  1. 生命周期(Lifetimes):理解引用如何被约束以保持有效。
  2. Trait 与泛型:Rust 的接口与多态机制。
  3. 错误处理ResultOption 的深度用法。
  4. 闭包与迭代器:函数式编程特性。

在下一节中,我们将深入 所有权、借用与生命周期,并结合 Solana 开发中的实际场景进行演练。