返回 SC 笔记
SC Day 2

Rust 开发环境 + 变量与可变性 + 基本类型

安装 Rust 工具链,理解 Cargo 项目结构,掌握变量/可变性/常量,学习基本数据类型

2026-04-11
第一阶段:基础构建
rustrustupcargovariablesmutabilitytypes

日期: 2026-04-11 方向: Rust 阶段: 第一阶段:基础构建 标签: #rust #rustup #cargo #variables #mutability #types


今日目标

类型内容
学习安装 Rust 工具链,理解 Cargo 项目结构,掌握变量/可变性/常量,学习基本数据类型
实操创建第一个 Rust 项目,完成 rustlings 的 intro 和 variables 练习
产出一个完整的 Rust 项目 + rustlings 练习通过记录

一、Rust 安装与环境配置

1.1 为什么学 Rust?

在区块链领域,Rust 的地位日益重要:

  • Solana 的链上程序用 Rust 编写
  • Polkadot/Substrate 框架基于 Rust
  • Aptos/Sui 的 Move 语言受 Rust 影响深远
  • Near Protocol 支持 Rust 编写智能合约
  • Cosmos SDK 的 CosmWasm 智能合约用 Rust
  • Rust 的内存安全零成本抽象让它成为区块链底层开发首选

1.2 安装 Rustup

Rustup 是 Rust 的官方工具链管理器,类似于 Node.js 的 nvm。

Windows 安装

# 访问 https://rustup.rs 下载 rustup-init.exe
# 或使用 PowerShell:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 安装完成后验证
rustc --version   # 应显示 rustc 1.7x.0 或更高
cargo --version   # Cargo 是 Rust 的包管理器和构建工具
rustup --version  # 工具链管理器

macOS/Linux 安装

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

1.3 核心工具介绍

工具作用类比
rustcRust 编译器solc (Solidity编译器)
cargo包管理器+构建工具npm + webpack
rustup工具链版本管理nvm
rustfmt代码格式化prettier
clippy代码静态检查eslint

1.4 IDE 推荐

  • VS Code + rust-analyzer 扩展(推荐):自动补全、类型推导、错误提示
  • RustRover(JetBrains):商业 IDE,功能更全但更重

二、Cargo 项目结构

2.1 创建项目

# 创建新项目(二进制可执行文件)
cargo new my_first_rust
cd my_first_rust

# 或创建库项目
cargo new my_lib --lib

2.2 项目目录结构

my_first_rust/
├── Cargo.toml          # 项目配置文件(类似 package.json)
├── Cargo.lock          # 依赖锁定文件(类似 package-lock.json)
├── src/
│   └── main.rs         # 入口文件
├── tests/              # 集成测试目录
├── benches/            # 性能基准测试
├── examples/           # 示例代码
└── target/             # 编译输出(.gitignore 中)

2.3 Cargo.toml 详解

[package]
name = "my_first_rust"          # 项目名
version = "0.1.0"               # 语义化版本
edition = "2021"                 # Rust 版本年份(2015/2018/2021)
authors = ["Your Name <email>"]
description = "My first Rust project for blockchain learning"

[dependencies]
# 外部依赖写在这里,类似 package.json 的 dependencies
# serde = "1.0"                 # JSON 序列化/反序列化
# tokio = { version = "1", features = ["full"] }  # 异步运行时

2.4 常用 Cargo 命令

cargo new project_name    # 创建新项目
cargo build               # 编译(debug 模式)
cargo build --release     # 编译(release 优化模式)
cargo run                 # 编译并运行
cargo test                # 运行测试
cargo check               # 快速检查代码(不生成二进制)
cargo clippy              # 运行 lint 检查
cargo fmt                 # 格式化代码
cargo doc --open          # 生成并打开文档
cargo add serde           # 添加依赖(需要 cargo-edit)

三、变量与可变性

这是 Rust 最与众不同的第一个特性:变量默认不可变(immutable)

3.1 let 声明(不可变变量)

fn main() {
    let x = 5;
    println!("x = {}", x);

    // x = 6;  // ❌ 编译错误!cannot assign twice to immutable variable
    // error[E0384]: cannot assign twice to immutable variable `x`
}

为什么默认不可变? 这是 Rust 的核心设计哲学——如果一个值不需要改变,就不应该能被改变。这能:

  1. 防止意外修改导致的 bug
  2. 让编译器做更好的优化
  3. 让并发编程更安全

3.2 let mut 声明(可变变量)

fn main() {
    let mut count = 0;
    println!("count = {}", count);  // 0

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

    count = 100;
    println!("count = {}", count);  // 100
}

3.3 const 常量

// 常量:必须注明类型,必须是编译时常量表达式
const MAX_SUPPLY: u64 = 21_000_000;
const PI: f64 = 3.141592653589793;
const CONTRACT_NAME: &str = "MyToken";

fn main() {
    println!("Max supply: {}", MAX_SUPPLY);
    // MAX_SUPPLY = 100;  // ❌ 常量永远不可变
}

const vs let 的区别

特性constletlet mut
可变性永远不可变不可变可变
类型注解必须可选(类型推导)可选
编译时求值
作用域任意(常在全局)函数内函数内
可以用函数返回值?

3.4 Shadowing(遮蔽)

Rust 的一个独特特性——用同名变量"遮蔽"前一个变量:

fn main() {
    let x = 5;
    let x = x + 1;    // 新的 x 遮蔽了旧的 x
    let x = x * 2;    // 再次遮蔽
    println!("x = {}", x);  // 12

    // Shadowing 甚至可以改变类型!
    let spaces = "   ";         // &str 类型
    let spaces = spaces.len();  // usize 类型,这是合法的!
    println!("spaces = {}", spaces);  // 3

    // 但 mut 不能改变类型:
    // let mut spaces = "   ";
    // spaces = spaces.len();  // ❌ 类型不匹配!
}

Shadowing vs mut 的选择

  • 需要改变类型时,用 Shadowing
  • 需要频繁修改同一个值时,用 mut
  • 只是做一次转换时,用 Shadowing

四、基本数据类型

4.1 整数类型

长度有符号无符号范围
8-biti8u8i8: -128 ~ 127, u8: 0 ~ 255
16-biti16u16...
32-biti32u32i32 是默认整数类型
64-biti64u64...
128-biti128u128适合大数计算
archisizeusize与平台位数相同(32/64位)
fn main() {
    // 默认整数类型是 i32
    let age = 25;              // i32
    let balance: u64 = 1_000_000_000; // 下划线提高可读性

    // 不同进制
    let hex = 0xff;            // 十六进制: 255
    let octal = 0o77;          // 八进制: 63
    let binary = 0b1111_0000;  // 二进制: 240
    let byte_val = b'A';       // 字节(u8): 65

    // 类型后缀
    let x = 42u8;              // 明确指定 u8
    let y = 100_i64;           // 明确指定 i64

    // 溢出行为
    // Debug 模式:溢出会 panic
    // Release 模式:溢出会 wrapping(环绕)
    // 推荐使用明确的方法:
    let a: u8 = 250;
    let b = a.wrapping_add(10);    // 环绕: 4
    let c = a.checked_add(10);     // 返回 Option: None
    let d = a.saturating_add(10);  // 饱和: 255
    let (e, overflowed) = a.overflowing_add(10); // (4, true)

    println!("wrapping: {}, checked: {:?}, saturating: {}, overflowing: ({}, {})",
        b, c, d, e, overflowed);
}

与 Solidity 对比

特性SolidityRust
默认整数uint256 (256位)i32 (32位)
溢出保护0.8.0+ 自动 revertDebug: panic, Release: wrap
最大整数uint256 (2^256-1)u128 (2^128-1)
无符号默认uint (无符号)i32 (有符号)

4.2 浮点类型

fn main() {
    let x = 2.0;          // f64(默认)
    let y: f32 = 3.0;     // f32

    // 浮点运算
    let sum = 5.0 + 10.0;
    let difference = 95.5 - 4.3;
    let product = 4.0 * 30.0;
    let quotient = 56.7 / 32.2;
    let truncated = -5.0_f64 / 3.0; // -1.6666...

    println!("sum={}, diff={}, prod={}, quot={}, trunc={}",
        sum, difference, product, quotient, truncated);
}

注意:Solidity 没有浮点数!在区块链世界中,精度问题可能导致资产损失,所以使用整数 + 放大倍数。Rust 虽然支持浮点数,但在链上程序(如 Solana)中也推荐使用整数。

4.3 布尔类型

fn main() {
    let t = true;
    let f: bool = false;

    // 布尔运算
    let and = t && f;   // false
    let or = t || f;    // true
    let not = !t;       // false

    println!("and={}, or={}, not={}", and, or, not);
}

4.4 字符类型

fn main() {
    let c = 'z';
    let z: char = 'ℤ';
    let heart = '❤';
    let chinese = '中';

    // char 在 Rust 中是 4 字节(Unicode 标量值)
    // 与 Solidity 不同,Solidity 没有单独的字符类型
    println!("Size of char: {} bytes", std::mem::size_of::<char>()); // 4
}

4.5 元组 (Tuple)

fn main() {
    // 元组:固定长度,可以包含不同类型
    let tup: (i32, f64, u8) = (500, 6.4, 1);

    // 解构
    let (x, y, z) = tup;
    println!("x={}, y={}, z={}", x, y, z);

    // 通过索引访问
    let first = tup.0;   // 500
    let second = tup.1;  // 6.4
    let third = tup.2;   // 1

    // 空元组 = unit type
    let unit: () = ();
    // 类似于其他语言的 void,函数没有返回值时隐式返回 ()
}

4.6 数组 (Array)

fn main() {
    // 数组:固定长度,相同类型
    let arr = [1, 2, 3, 4, 5];
    let arr_typed: [i32; 5] = [1, 2, 3, 4, 5];

    // 初始化所有元素为同一个值
    let zeros = [0; 10];     // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    let ones = [1u8; 32];    // 32 个 1,类型 [u8; 32](类似 bytes32)

    // 访问元素
    let first = arr[0];      // 1
    let second = arr[1];     // 2

    // 越界访问会 panic(运行时检查)
    // let out_of_bounds = arr[10];  // ❌ 运行时 panic!

    // 遍历
    for element in arr.iter() {
        print!("{} ", element);
    }
    println!();

    // 数组长度
    println!("Length: {}", arr.len());  // 5

    // 切片
    let slice = &arr[1..3];  // [2, 3]
    println!("Slice: {:?}", slice);
}

Rust 数组 vs Solidity 数组

特性Rust ArraySolidity Fixed ArraySolidity Dynamic Array
语法[T; N]T[N]T[]
长度编译时固定编译时固定运行时动态
存储位置storage/memorystorage/memory
越界检查运行时 panic运行时 revert运行时 revert

4.7 Vector(动态数组)

fn main() {
    // Vec<T> - Rust 的动态数组(类似 Solidity 的动态数组)
    let mut v: Vec<i32> = Vec::new();
    v.push(1);
    v.push(2);
    v.push(3);

    // 使用宏快速创建
    let v2 = vec![10, 20, 30];

    // 访问
    let third = &v[2];                    // 直接索引(可能 panic)
    let third_safe = v.get(2);            // 返回 Option<&i32>(安全)

    match v.get(100) {
        Some(val) => println!("Got: {}", val),
        None => println!("Index out of bounds!"),
    }

    // 遍历
    for i in &v {
        print!("{} ", i);
    }
    println!();

    // 修改遍历
    for i in &mut v {
        *i += 100;
    }
    println!("{:?}", v);  // [101, 102, 103]
}

五、代码实战:综合练习

/// Rust Day 2 综合练习:模拟代币基本信息
/// 对标 Solidity Day 1 的 DataTypes 合约

const MAX_SUPPLY: u64 = 21_000_000;
const TOKEN_NAME: &str = "MomoToken";
const TOKEN_SYMBOL: &str = "MOMO";
const DECIMALS: u8 = 18;

fn main() {
    println!("=== Token Info ===");
    println!("Name: {}", TOKEN_NAME);
    println!("Symbol: {}", TOKEN_SYMBOL);
    println!("Decimals: {}", DECIMALS);
    println!("Max Supply: {}", MAX_SUPPLY);

    // 模拟余额(使用 HashMap 类似 Solidity 的 mapping)
    use std::collections::HashMap;
    let mut balances: HashMap<&str, u64> = HashMap::new();

    // 模拟 mint
    let alice = "0xAlice";
    let bob = "0xBob";
    balances.insert(alice, 1_000_000);
    balances.insert(bob, 500_000);

    println!("\n=== Balances ===");
    for (addr, balance) in &balances {
        println!("{}: {} {}", addr, balance, TOKEN_SYMBOL);
    }

    // 模拟 transfer
    let transfer_amount: u64 = 100_000;
    println!("\n=== Transfer {} {} from Alice to Bob ===", transfer_amount, TOKEN_SYMBOL);

    // 使用 checked 算术防止溢出
    if let Some(alice_balance) = balances.get(alice).copied() {
        if alice_balance >= transfer_amount {
            let new_alice = alice_balance.checked_sub(transfer_amount).unwrap();
            let bob_balance = balances.get(bob).copied().unwrap_or(0);
            let new_bob = bob_balance.checked_add(transfer_amount).unwrap();

            balances.insert(alice, new_alice);
            balances.insert(bob, new_bob);

            println!("Transfer successful!");
        } else {
            println!("Insufficient balance!");
        }
    }

    println!("\n=== Updated Balances ===");
    for (addr, balance) in &balances {
        println!("{}: {} {}", addr, balance, TOKEN_SYMBOL);
    }

    // 类型演示
    println!("\n=== Type Demonstration ===");
    demonstrate_types();

    // 可变性演示
    println!("\n=== Mutability Demonstration ===");
    demonstrate_mutability();
}

fn demonstrate_types() {
    // 整数
    let small: u8 = 255;
    let default_int = 42;           // i32
    let big: u128 = 340_282_366_920_938_463_463_374_607_431_768_211_455;
    let negative: i32 = -100;

    // 浮点
    let pi: f64 = 3.14159;

    // 布尔
    let is_active = true;

    // 字符
    let emoji = '🦀';

    // 元组
    let token_info: (&str, &str, u8) = ("MomoToken", "MOMO", 18);

    // 数组
    let top_holders: [u64; 3] = [1_000_000, 500_000, 250_000];

    println!("u8 max: {}", small);
    println!("default int: {} (i32)", default_int);
    println!("u128 max: {}", big);
    println!("negative: {}", negative);
    println!("pi: {}", pi);
    println!("is_active: {}", is_active);
    println!("emoji: {}", emoji);
    println!("token name: {}", token_info.0);
    println!("top holder #1: {}", top_holders[0]);
}

fn demonstrate_mutability() {
    // 不可变(默认)
    let x = 5;
    println!("x = {}", x);

    // 可变
    let mut counter = 0;
    counter += 1;
    counter += 1;
    println!("counter = {}", counter);

    // Shadowing
    let value = "123";           // &str
    let value = value.parse::<i32>().unwrap(); // i32,类型改变
    let value = value * 2;       // i32
    println!("value = {}", value);  // 246

    // const
    const GENESIS_BLOCK: u64 = 0;
    println!("Genesis block: {}", GENESIS_BLOCK);
}

六、Rustlings 练习指南

6.1 安装 Rustlings

# 安装 rustlings
cargo install rustlings
rustlings init
cd rustlings

# 开始练习
rustlings

6.2 intro 练习

// intro1.rs - 你的第一个 Rust 程序
// 只需要输出 "Hello and welcome to Rust!"
fn main() {
    println!("Hello and welcome to Rust!");
}

// intro2.rs - 格式化输出
fn main() {
    let x = 5;
    let y = 10;
    println!("x = {} and y = {}", x, y);
    // {} 是占位符,类似 Solidity event 中的参数
}

6.3 variables 练习

// variables1.rs - 变量绑定需要 let
fn main() {
    let x = 5;
    println!("x has the value {}", x);
}

// variables2.rs - 类型注解
fn main() {
    let x: i32 = 10;
    println!("x = {}", x);
}

// variables3.rs - 可变性
fn main() {
    let mut x = 3;
    println!("x = {}", x);
    x = 5;
    println!("x = {}", x);
}

// variables4.rs - 常量
const NUMBER: i32 = 3;
fn main() {
    println!("Number: {}", NUMBER);
}

// variables5.rs - Shadowing
fn main() {
    let number = "T-H-R-E-E";
    println!("Spell: {}", number);
    let number = 3;
    println!("Number: {}", number);
}

// variables6.rs - 类型推断
fn main() {
    let number: i32 = 5;
    println!("Number: {}", number);
}

七、关键要点总结

要点说明
Rust 变量默认不可变需要 mut 关键字才能修改
Shadowing 可以改变类型let x = "5"; let x = x.parse::<i32>();
默认整数是 i32不同于 Solidity 的 uint256
没有 nullOption<T> 代替(Some/None)
数组固定长度动态用 Vec<T>
checked 算术checked_add 防止溢出
Cargo 是一切构建、测试、依赖管理、文档

八、常见误区

误区 1:忘记加 mut

let x = 5;
// x = 6;  // ❌ 编译器会给出非常友好的错误提示
// 建议:help: consider making this binding mutable: `let mut x`

误区 2:类型不匹配

let x: i32 = 5;
let y: u32 = 10;
// let z = x + y;  // ❌ 不同整数类型不能直接运算
let z = x + y as i32;  // ✅ 需要显式转换

误区 3:数组越界

let arr = [1, 2, 3];
// let val = arr[5];  // ❌ 编译通过但运行时 panic!
// 推荐使用 .get() 方法返回 Option
let val = arr.get(5);  // ✅ 返回 None

误区 4:把 Rust 的 String 当成其他语言的 string

// Rust 有两种字符串:
let s1: &str = "hello";         // 字符串切片(不可变引用)
let s2: String = String::from("hello"); // 堆分配的字符串
// Day 6 会详细讲解它们的区别

九、Solidity vs Rust 类型对照表

概念SolidityRust备注
无符号整数uint256u64 / u128Solidity 默认 256 位
有符号整数int256i32 / i64Rust 默认 32 位
布尔boolbool相同
地址address (20 bytes)[u8; 32] (Solana)链依赖
字符串stringString / &strRust 区分所有权
固定字节bytes32[u8; 32]类似
动态数组uint[]Vec<u32>类似
映射mapping(K => V)HashMap<K, V>标准库
枚举enum Status {}enum Status {}Rust 枚举更强大
常量constantconst编译时确定
不可变immutable默认行为Rust 默认不可变

十、面试关联

Q: Rust 为什么选择变量默认不可变?

A: 这是 Rust 安全性设计的基石。不可变性让编译器可以保证数据不会被意外修改,在并发场景下尤其重要——多个线程可以安全地共享不可变数据而无需锁。在区块链上下文中,这有助于防止状态被意外修改导致的安全漏洞。

Q: Rust 的 u128 够用吗?区块链常需要 uint256。

A: Rust 原生最大支持 u128(2^128-1),不如 Solidity 的 uint256。但实际中:Solana 使用 u64 表示代币数量(足够表示天文数字的 lamports),大数运算可以用 uint crate 或自定义实现。Move 语言同样使用 u128 作为最大原生整数。

Q: Cargo 和 npm 有什么关键区别?

A: Cargo 不仅是包管理器(类似 npm),还是构建系统(类似 webpack)、测试运行器(类似 jest)和文档生成器。它的依赖解析更严格,编译是增量的,且内置了 cargo clippy(lint)和 cargo fmt(格式化)。这种一体化工具链设计让 Rust 的开发体验非常统一。


十一、参考资源

资源链接说明
The Rust Bookhttps://doc.rust-lang.org/book/官方教程,必读
Rustlingshttps://github.com/rust-lang/rustlings交互式练习
Rust by Examplehttps://doc.rust-lang.org/rust-by-example/代码示例大全
Rust Playgroundhttps://play.rust-lang.org/在线运行 Rust
Cargo Bookhttps://doc.rust-lang.org/cargo/Cargo 详细指南
Solana Cookbookhttps://solanacookbook.com/Solana 开发参考