Skip to content

从Python到Rust - 1

约 8552 字大约 29 分钟

rustpython

2025-09-17

第1章:环境搭建与第一个程序

1.1 开发环境对比

作为Python开发者,你可能已经习惯了使用pipcondapoetry来管理Python环境和依赖。Rust生态系统有自己的一套工具链,让我们来对比了解:

Python环境管理

# Python环境管理
python --version           # 检查版本
pip install package       # 安装包
pip freeze > requirements.txt  # 导出依赖
python -m venv venv       # 创建虚拟环境

Rust环境管理

# Rust环境管理
rustc --version           # 检查编译器版本
cargo --version           # 检查包管理器版本
cargo new project_name    # 创建新项目
cargo build              # 编译项目
cargo run                # 编译并运行

1.2 安装Rust

在macOS/Linux上安装

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

在Windows上安装

下载并运行 rustup-init.exe from https://rustup.rs/

验证安装

rustc --version
cargo --version
rustup --version

与Python对比:

  • Python通常需要分别安装解释器和包管理器
  • Rust的rustup同时管理编译器、标准库和工具链
  • rustup类似于pyenv,可以管理多个Rust版本

1.3 包管理器对比

特性Python (pip/poetry)Rust (cargo)
配置文件requirements.txt / pyproject.tomlCargo.toml
锁定文件poetry.lockCargo.lock
安装依赖pip installcargo add
运行项目python main.pycargo run
构建项目N/A (解释型)cargo build
测试pytestcargo test
发布包pip uploadcargo publish

1.4 创建第一个Rust项目

使用Cargo创建项目

cargo new hello_rust
cd hello_rust

这会创建以下目录结构:

hello_rust/
├── Cargo.toml      # 项目配置文件(类似Python的pyproject.toml)
└── src/
    └── main.rs     # 主程序文件

Cargo.toml文件解析

[package]
name = "hello_rust"    # 项目名称
version = "0.1.0"      # 版本号
edition = "2021"       # Rust版本

[dependencies]
# 这里添加依赖,类似于requirements.txt

与Python对比:

# Python的pyproject.toml
[project]
name = "hello_python"
version = "0.1.0"
dependencies = [
    "requests>=2.25.0",
]

1.5 第一个程序:Hello World

Rust版本

// src/main.rs
fn main() {
    println!("Hello, world!");
}

Python版本对比

# main.py
print("Hello, world!")

运行程序

Rust:

cargo run

Python:

python main.py

1.6 编译 vs 解释执行

这是Rust和Python最重要的区别之一:

Python(解释型语言)

# main.py
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    print(greet("Python"))

运行过程:

  1. Python解释器逐行读取代码
  2. 实时解释执行
  3. 错误在运行时发现

Rust(编译型语言)

// src/main.rs
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

fn main() {
    println!("{}", greet("Rust"));
}

运行过程:

  1. cargo build - 编译成机器码
  2. 生成可执行文件 ./target/debug/hello_rust
  3. 错误在编译时发现
  4. 运行时性能更高

对比分析

特性PythonRust
执行方式解释执行编译执行
启动速度慢(需要编译)
运行速度
错误发现运行时编译时
部署需要Python环境独立可执行文件
开发迭代慢(编译时间)

1.7 基本项目结构

标准Rust项目结构

my_project/
├── Cargo.toml          # 项目配置
├── Cargo.lock          # 依赖锁定文件
├── src/
│   ├── main.rs         # 二进制程序入口
│   ├── lib.rs          # 库入口(可选)
│   └── bin/            # 额外的二进制文件
├── tests/              # 集成测试
├── examples/           # 示例代码
└── benches/           # 性能测试

对比Python项目结构

my_project/
├── pyproject.toml      # 项目配置
├── requirements.txt    # 依赖列表
├── src/
│   ├── __init__.py
│   └── main.py
├── tests/
│   └── test_main.py
└── docs/

1.8 开发环境推荐

IDE和编辑器

  1. VS Code + rust-analyzer - 推荐给Python开发者
  2. IntelliJ IDEA + Rust Plugin
  3. Vim/Neovim + rust-analyzer

有用的Cargo命令

cargo new project_name     # 创建新项目
cargo build               # 编译项目
cargo run                 # 编译并运行
cargo test                # 运行测试
cargo check               # 快速检查代码(不生成可执行文件)
cargo clippy              # 代码检查(类似于pylint)
cargo fmt                 # 代码格式化(类似于black)
cargo doc --open          # 生成并打开文档

Python等价命令对比

# Python                    # Rust
python main.py             cargo run
python -m pytest          cargo test
pylint main.py             cargo clippy
black main.py              cargo fmt
pip install package        cargo add package

1.9 实践练习

练习1:创建一个简单的计算器

// src/main.rs
use std::io;

fn main() {
    println!("简单计算器");
    println!("输入第一个数字:");
    
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("读取输入失败");
    let num1: f64 = input.trim().parse().expect("无效数字");
    
    println!("输入第二个数字:");
    input.clear();
    io::stdin().read_line(&mut input).expect("读取输入失败");
    let num2: f64 = input.trim().parse().expect("无效数字");
    
    println!("{} + {} = {}", num1, num2, num1 + num2);
    println!("{} - {} = {}", num1, num2, num1 - num2);
    println!("{} * {} = {}", num1, num2, num1 * num2);
    println!("{} / {} = {}", num1, num2, num1 / num2);
}

对比Python版本:

# main.py
print("简单计算器")
num1 = float(input("输入第一个数字:"))
num2 = float(input("输入第二个数字:"))

print(f"{num1} + {num2} = {num1 + num2}")
print(f"{num1} - {num2} = {num1 - num2}")
print(f"{num1} * {num2} = {num1 * num2}")
print(f"{num1} / {num2} = {num1 / num2}")

关键差异分析:

  1. 类型声明:Rust需要显式类型转换
  2. 错误处理:Rust强制处理可能的错误
  3. 内存管理:Rust的String::new().clear()
  4. 模式匹配:Rust的expect()vs Python的异常处理

1.10 常见问题和解决方案

问题1:编译错误 vs 运行时错误

Python中的运行时错误:

def divide(a, b):
    return a / b  # 如果b=0,运行时才报错

result = divide(10, 0)  # ZeroDivisionError

Rust中的编译时检查:

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None  // 编译时就要求处理这种情况
    } else {
        Some(a / b)
    }
}

fn main() {
    match divide(10.0, 0.0) {
        Some(result) => println!("结果: {}", result),
        None => println!("不能除以零"),
    }
}

问题2:包管理混乱

在Python中,我们经常遇到依赖冲突:

pip install package_a  # 需要 requests==2.25.0
pip install package_b  # 需要 requests==2.28.0  # 冲突!

Rust的Cargo.lock确保版本一致性:

# Cargo.toml
[dependencies]
serde = "1.0"
tokio = "1.0"

1.11 性能对比示例

让我们通过一个简单的斐波那契数列计算来对比性能:

Python版本

import time

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

start = time.time()
result = fibonacci(35)
end = time.time()
print(f"结果: {result}, 耗时: {end - start:.2f}秒")

Rust版本

use std::time::Instant;

fn fibonacci(n: u32) -> u32 {
    if n <= 1 {
        return n;
    }
    fibonacci(n - 1) + fibonacci(n - 2)
}

fn main() {
    let start = Instant::now();
    let result = fibonacci(35);
    let duration = start.elapsed();
    println!("结果: {}, 耗时: {:.2?}", result, duration);
}

典型性能差异:

  • Python: ~3-5秒
  • Rust: ~0.1-0.3秒

这展示了编译型语言的性能优势。

1.12 本章小结

通过本章学习,你应该掌握:

  1. 环境搭建:rustup, cargo的使用
  2. 项目结构:理解Cargo.toml和标准目录结构
  3. 基本概念:编译vs解释的区别
  4. 开发流程:从编写代码到运行程序的完整流程
  5. 工具对比:Rust工具链vs Python工具链

关键要点:

  • Rust是编译型语言,提供更好的性能和类型安全
  • Cargo是强大的包管理器和构建工具
  • 错误在编译时就被发现,提高代码质量
  • 学习曲线较Python陡峭,但带来更多控制权

下一章预告: 我们将深入学习Rust的基础语法和数据类型,重点对比与Python的差异。


第2章:基础语法与数据类型

2.1 变量声明与可变性

这是Rust与Python最重要的区别之一:Rust默认变量不可变,而Python变量默认可变。

Python变量(默认可变)

# Python中变量默认可变
name = "Alice"
name = "Bob"          # ✓ 可以重新赋值
age = 25
age = age + 1         # ✓ 可以修改

Rust变量(默认不可变)

// Rust中变量默认不可变
let name = "Alice";
// name = "Bob";      // ✗ 编译错误:cannot assign twice to immutable variable

let age = 25;
// age = age + 1;     // ✗ 编译错误

// 使用 mut 关键字声明可变变量
let mut mutable_age = 25;
mutable_age = mutable_age + 1;  // ✓ 正确

变量遮蔽 (Shadowing)

Rust允许变量遮蔽,这在Python中没有直接对应概念:

let x = 5;
let x = x + 1;        // 创建新变量,遮蔽旧变量
let x = x * 2;        // 再次遮蔽
println!("x = {}", x); // 输出:x = 12

// 可以改变类型
let spaces = "   ";
let spaces = spaces.len(); // 从&str变为usize

对比Python:

x = 5
x = x + 1             # 直接修改变量
x = x * 2
print(f"x = {x}")     # 输出:x = 12

# Python中改变类型需要注意
spaces = "   "
spaces = len(spaces)  # 类型从str变为int,但运行时才知道

2.2 基础数据类型对比

整数类型

Rust类型大小Python等价范围
i88位int-128 到 127
i1616位int-32,768 到 32,767
i3232位int-2^31 到 2^31-1
i6464位int-2^63 到 2^63-1
i128128位int-2^127 到 2^127-1
isize指针大小int取决于架构
u88位无符号int0 到 255
u1616位无符号int0 到 65,535
u3232位无符号int0 到 2^32-1
u6464位无符号int0 到 2^64-1
u128128位无符号int0 到 2^128-1
usize指针大小无符号int取决于架构
// Rust - 显式类型
let small_number: i8 = 127;
let big_number: i64 = 1_000_000;
let hex_number = 0xff;           // 255
let octal_number = 0o77;         // 63
let binary_number = 0b1111_0000; // 240

// 溢出在debug模式下会panic
let mut overflow: u8 = 255;
// overflow += 1; // panic in debug mode
# Python - 动态类型,自动大整数
small_number = 127
big_number = 1_000_000
hex_number = 0xff
octal_number = 0o77
binary_number = 0b11110000

# Python整数可以任意大
huge_number = 10**100  # 没问题

浮点类型

// Rust有两种浮点类型
let float32: f32 = 3.14;
let float64: f64 = 2.718281828; // 默认类型

// 科学计数法
let scientific = 1e6;          // f64类型
# Python只有一种浮点类型(类似f64)
float32 = 3.14
float64 = 2.718281828

# 科学计数法
scientific = 1e6

布尔类型

// Rust布尔类型
let is_true: bool = true;
let is_false = false;

// Rust布尔值不能隐式转换为整数
// let number = is_true + 1; // ✗ 编译错误
let number = is_true as i32 + 1; // ✓ 显式转换
# Python布尔类型
is_true = True
is_false = False

# Python布尔值可以当作整数使用
number = is_true + 1  # 结果为2,因为True == 1

字符类型

// Rust字符类型(4字节Unicode)
let char_a = 'a';
let char_unicode = '';
let char_emoji = '😀';

// 字符串字面量
let string_literal = "Hello, world!";
# Python没有单独的字符类型
char_a = 'a'          # 这是长度为1的字符串
char_unicode = ''
char_emoji = '😀'

string_literal = "Hello, world!"

2.3 字符串类型详解

这是Rust最复杂的部分之一,因为Rust有多种字符串类型:

Rust字符串类型

// 1. 字符串字面量 &str(不可变,借用)
let string_literal: &str = "Hello, world!";

// 2. String类型(可变,拥有所有权)
let mut owned_string = String::new();
owned_string.push_str("Hello");
owned_string.push(' ');
owned_string.push_str("world!");

// 3. 从字面量创建String
let another_string = String::from("Hello, Rust!");
let yet_another = "Hello, Rust!".to_string();

// 4. 字符串切片
let slice = &another_string[0..5]; // "Hello"

Python字符串(简单统一)

# Python只有一种字符串类型
string_literal = "Hello, world!"

# 字符串操作
owned_string = ""
owned_string += "Hello"
owned_string += " "
owned_string += "world!"

another_string = "Hello, Python!"

# 字符串切片
slice_str = another_string[0:5]  # "Hello"

字符串操作对比

// Rust字符串操作
let s1 = String::from("Hello");
let s2 = String::from("World");

// 连接字符串
let s3 = s1 + &s2;        // s1被移动,不能再使用
let s4 = format!("{} {}", s2, "Rust"); // 推荐方式

// 字符串比较
let are_equal = s2 == "World";

// 字符串长度
let length = s2.len();    // 字节长度
let char_count = s2.chars().count(); // 字符数量

// 遍历字符
for ch in s2.chars() {
    println!("{}", ch);
}
# Python字符串操作
s1 = "Hello"
s2 = "World"

# 连接字符串
s3 = s1 + s2
s4 = f"{s2} Python"     # f-string

# 字符串比较
are_equal = s2 == "World"

# 字符串长度
length = len(s2)        # 字符数量

# 遍历字符
for ch in s2:
    print(ch)

2.4 类型推断与显式类型标注

Rust类型推断

// Rust有强大的类型推断
let number = 42;          // 推断为i32
let pi = 3.14;           // 推断为f64
let text = "hello";      // 推断为&str

// 但有时需要显式标注
let numbers: Vec<i32> = vec![1, 2, 3];
let parsed: i32 = "42".parse().expect("Not a number");

// 类型后缀
let explicit_float = 3.14f32;
let explicit_int = 42u64;

Python类型提示(可选)

# Python类型提示是可选的
number = 42              # 运行时是int
pi = 3.14               # 运行时是float
text = "hello"          # 运行时是str

# 类型提示(仅用于静态检查)
from typing import List
numbers: List[int] = [1, 2, 3]
parsed: int = int("42")

# Python没有类型后缀
explicit_float = float(3.14)
explicit_int = int(42)

2.5 常量与静态变量

Rust常量

// 编译时常量
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159265359;

// 静态变量(全局)
static HELLO_WORLD: &str = "Hello, world!";
static mut COUNTER: u32 = 0; // 可变静态变量(unsafe)

fn main() {
    println!("{}", MAX_POINTS);
    println!("{}", HELLO_WORLD);
    
    // 访问可变静态变量需要unsafe
    unsafe {
        COUNTER += 1;
        println!("Counter: {}", COUNTER);
    }
}

Python常量(约定)

# Python没有真正的常量,只是约定
MAX_POINTS = 100_000  # 约定不修改
PI = 3.14159265359

# 全局变量
hello_world = "Hello, world!"
counter = 0

def main():
    global counter
    print(MAX_POINTS)
    print(hello_world)
    
    counter += 1     # 可以修改
    print(f"Counter: {counter}")

2.6 类型转换

Rust类型转换(显式)

// Rust要求显式类型转换
let integer = 42;
let float = integer as f64;        // 数值转换
let back_to_int = float as i32;

// 字符串转换
let number_str = "42";
let parsed_number: i32 = number_str.parse().unwrap();
let number_to_str = parsed_number.to_string();

// TryFrom/TryInto trait(安全转换)
use std::convert::TryFrom;
let big_number: i64 = 1000;
let small_number = i32::try_from(big_number).unwrap();

// From/Into trait(无损转换)
let string_from_str = String::from("hello");
let vec_from_array: Vec<i32> = vec![1, 2, 3];

Python类型转换(隐式+显式)

# Python有更多隐式转换
integer = 42
float_val = float(integer)         # 显式转换
result = integer + 3.14            # 隐式转换为float

# 字符串转换
number_str = "42"
parsed_number = int(number_str)
number_to_str = str(parsed_number)

# Python的动态类型转换
big_number = 1000
small_number = big_number          # 没有溢出问题

# 自动类型转换
string_from_int = str(42)
list_from_tuple = list((1, 2, 3))

2.7 数组与集合类型入门

固定长度数组

// Rust固定长度数组
let array: [i32; 5] = [1, 2, 3, 4, 5];
let same_value = [3; 5];           // [3, 3, 3, 3, 3]

// 数组访问
let first = array[0];
let slice = &array[1..3];          // 切片:[2, 3]

// 数组长度在编译时确定
println!("Array length: {}", array.len());
# Python没有固定长度数组概念,都是动态列表
array = [1, 2, 3, 4, 5]
same_value = [3] * 5               # [3, 3, 3, 3, 3]

# 列表访问
first = array[0]
slice_list = array[1:3]            # [2, 3]

# 列表长度在运行时确定
print(f"List length: {len(array)}")

动态数组Vector

// Rust动态数组
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec.push(3);

// 宏创建
let vec2 = vec![1, 2, 3, 4, 5];

// Vector操作
vec.pop();                         // 返回Option<T>
let length = vec.len();
vec.clear();
# Python列表(类似Vector)
vec = []
vec.append(1)
vec.append(2)
vec.append(3)

# 列表字面量
vec2 = [1, 2, 3, 4, 5]

# 列表操作
vec.pop()                          # 返回元素或抛出异常
length = len(vec)
vec.clear()

2.8 元组类型

// Rust元组
let tuple: (i32, f64, &str) = (42, 3.14, "hello");

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

// 访问元素
let first = tuple.0;
let second = tuple.1;

// 单元元组(空元组)
let unit: () = ();
# Python元组
tuple_val = (42, 3.14, "hello")

# 解构
x, y, z = tuple_val
print(f"x: {x}, y: {y}, z: {z}")

# 访问元素
first = tuple_val[0]
second = tuple_val[1]

# 空元组
unit = ()

2.9 实践练习

练习1:温度转换器

// Rust版本
use std::io;

fn celsius_to_fahrenheit(celsius: f64) -> f64 {
    celsius * 9.0 / 5.0 + 32.0
}

fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
    (fahrenheit - 32.0) * 5.0 / 9.0
}

fn main() {
    println!("温度转换器");
    println!("1. 摄氏度转华氏度");
    println!("2. 华氏度转摄氏度");
    
    let mut input = String::new();
    println!("请选择转换类型 (1 或 2):");
    io::stdin().read_line(&mut input).expect("读取输入失败");
    
    let choice: u32 = input.trim().parse().expect("无效选择");
    
    input.clear();
    println!("请输入温度值:");
    io::stdin().read_line(&mut input).expect("读取输入失败");
    
    let temperature: f64 = input.trim().parse().expect("无效温度");
    
    match choice {
        1 => {
            let result = celsius_to_fahrenheit(temperature);
            println!("{}°C = {:.2}°F", temperature, result);
        },
        2 => {
            let result = fahrenheit_to_celsius(temperature);
            println!("{}°F = {:.2}°C", temperature, result);
        },
        _ => println!("无效选择"),
    }
}
# Python版本
def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

def fahrenheit_to_celsius(fahrenheit):
    return (fahrenheit - 32) * 5 / 9

def main():
    print("温度转换器")
    print("1. 摄氏度转华氏度")
    print("2. 华氏度转摄氏度")
    
    choice = int(input("请选择转换类型 (1 或 2): "))
    temperature = float(input("请输入温度值: "))
    
    if choice == 1:
        result = celsius_to_fahrenheit(temperature)
        print(f"{temperature}°C = {result:.2f}°F")
    elif choice == 2:
        result = fahrenheit_to_celsius(temperature)
        print(f"{temperature}°F = {result:.2f}°C")
    else:
        print("无效选择")

if __name__ == "__main__":
    main()

练习2:数据类型探索

// Rust版本 - 展示不同数据类型
fn main() {
    // 整数类型
    let small: i8 = 127;
    let medium: i32 = 1_000_000;
    let large: i64 = 9_223_372_036_854_775_807;
    
    println!("整数类型:");
    println!("i8: {}", small);
    println!("i32: {}", medium);
    println!("i64: {}", large);
    
    // 浮点类型
    let pi: f32 = 3.14159;
    let e: f64 = 2.718281828459045;
    
    println!("\n浮点类型:");
    println!("f32: {}", pi);
    println!("f64: {}", e);
    
    // 字符和字符串
    let character = 'R';
    let string_slice = "Rust";
    let owned_string = String::from("Programming");
    
    println!("\n字符和字符串:");
    println!("char: {}", character);
    println!("&str: {}", string_slice);
    println!("String: {}", owned_string);
    
    // 布尔值
    let is_rust_awesome = true;
    println!("\n布尔值: {}", is_rust_awesome);
    
    // 数组和元组
    let array = [1, 2, 3, 4, 5];
    let tuple = (42, "answer", true);
    
    println!("\n集合类型:");
    println!("数组: {:?}", array);
    println!("元组: {:?}", tuple);
    
    // 类型大小
    use std::mem;
    println!("\n类型大小(字节):");
    println!("i32: {}", mem::size_of::<i32>());
    println!("f64: {}", mem::size_of::<f64>());
    println!("bool: {}", mem::size_of::<bool>());
    println!("char: {}", mem::size_of::<char>());
}
# Python版本 - 展示不同数据类型
import sys

def main():
    # 整数类型(Python只有一种int)
    small = 127
    medium = 1_000_000
    large = 9_223_372_036_854_775_807
    
    print("整数类型:")
    print(f"small int: {small}")
    print(f"medium int: {medium}")
    print(f"large int: {large}")
    
    # 浮点类型
    pi = 3.14159
    e = 2.718281828459045
    
    print("\n浮点类型:")
    print(f"float: {pi}")
    print(f"float: {e}")
    
    # 字符和字符串
    character = 'P'
    string_val = "Python"
    
    print("\n字符和字符串:")
    print(f"character: {character}")
    print(f"string: {string_val}")
    
    # 布尔值
    is_python_awesome = True
    print(f"\n布尔值: {is_python_awesome}")
    
    # 列表和元组
    list_val = [1, 2, 3, 4, 5]
    tuple_val = (42, "answer", True)
    
    print("\n集合类型:")
    print(f"列表: {list_val}")
    print(f"元组: {tuple_val}")
    
    # 对象大小
    print("\n对象大小(字节):")
    print(f"int: {sys.getsizeof(42)}")
    print(f"float: {sys.getsizeof(3.14)}")
    print(f"bool: {sys.getsizeof(True)}")
    print(f"str: {sys.getsizeof('a')}")

if __name__ == "__main__":
    main()

2.10 类型系统对比总结

特性RustPython
类型检查静态,编译时动态,运行时
类型推断强大的推断无(解释器推断)
变量可变性默认不可变默认可变
类型转换显式转换隐式+显式转换
整数溢出编译时/运行时检查自动大整数
字符串类型多种类型单一类型
内存安全编译时保证垃圾回收器

2.11 常见陷阱与最佳实践

陷阱1:整数溢出

// Rust中要注意溢出
let mut x: u8 = 255;
// x += 1; // Debug模式下panic,Release模式下环绕

// 安全的方式
x = x.saturating_add(1); // 饱和加法,结果为255
// 或者
match x.checked_add(1) {
    Some(result) => x = result,
    None => println!("溢出了!"),
}
# Python自动处理大整数
x = 255
x += 1  # 没问题,自动变为256

陷阱2:字符串所有权

// 理解字符串所有权
let s1 = String::from("hello");
let s2 = s1;                    // s1被移动到s2
// println!("{}", s1);          // ✗ 编译错误

// 正确的方式
let s1 = String::from("hello");
let s2 = s1.clone();           // 深拷贝
println!("{} {}", s1, s2);     // ✓ 正确

最佳实践

  1. 优先使用类型推断,除非需要明确指定
  2. 默认使用不可变变量,只在必要时使用mut
  3. 优先使用&str而不是String,除非需要所有权
  4. 使用checked_*方法处理可能溢出的算术运算
  5. 理解borrowing和ownership(下一章详细讲解)

2.12 本章小结

通过本章学习,你应该掌握:

  1. 变量声明let关键字和可变性概念
  2. 基础类型:整数、浮点、布尔、字符类型
  3. 字符串类型Stringvs&str的区别
  4. 类型转换:显式转换的必要性
  5. 集合类型:数组、Vector、元组的基础使用

关键差异总结:

  • Rust是静态类型语言,Python是动态类型
  • Rust变量默认不可变,Python变量默认可变
  • Rust要求显式类型转换,Python允许更多隐式转换
  • Rust有更细粒度的数值类型控制

下一章预告: 我们将学习Rust最核心的概念——所有权系统,这是Rust实现内存安全的关键机制。


第3章:所有权系统 - Rust的核心概念

3.1 什么是所有权系统

所有权(Ownership)是Rust最独特且重要的特性,它使得Rust能够在不使用垃圾回收器的情况下保证内存安全。对于Python开发者来说,这是一个全新的概念,因为Python依靠垃圾回收器自动管理内存。

Python的内存管理

# Python通过引用计数和垃圾回收管理内存
def create_data():
    data = [1, 2, 3, 4, 5]  # 创建列表
    return data             # 返回引用

my_data = create_data()     # 数据仍然存在,引用计数管理
another_ref = my_data       # 增加引用计数
# 当所有引用都消失时,垃圾回收器清理内存

Rust的所有权系统

// Rust通过所有权系统在编译时管理内存
fn create_data() -> Vec<i32> {
    let data = vec![1, 2, 3, 4, 5];  // 创建向量
    data                             // 转移所有权给调用者
}

fn main() {
    let my_data = create_data();     // 获得所有权
    // let another_ref = my_data;    // 这会转移所有权,my_data不再可用
    // println!("{:?}", my_data);    // 编译错误!
    
    // 正确的方式:借用
    let another_ref = &my_data;      // 借用引用
    println!("{:?}", my_data);       // 正确,所有权仍在my_data
    println!("{:?}", another_ref);   // 正确,这是借用
}

3.2 所有权规则

Rust的所有权系统有三个基本规则:

  1. 每个值都有一个唯一的所有者
  2. 同一时间只能有一个所有者
  3. 当所有者离开作用域时,值被自动清理

让我们通过例子来理解这些规则:

规则1:每个值都有唯一所有者

// Rust
let s = String::from("hello");  // s是字符串的所有者
# Python - 没有所有权概念,只有引用
s = "hello"  # s是对字符串对象的引用

规则2:同一时间只能有一个所有者

// Rust - 所有权转移
let s1 = String::from("hello");
let s2 = s1;                    // 所有权从s1转移到s2
// println!("{}", s1);          // 编译错误:s1不再拥有所有权
println!("{}", s2);             // 正确:s2现在是所有者
# Python - 多个引用可以指向同一对象
s1 = "hello"
s2 = s1      # s1和s2都引用同一个字符串对象
print(s1)    # 正常
print(s2)    # 正常

规则3:所有者离开作用域时自动清理

// Rust - 自动清理
{
    let s = String::from("hello");  // s进入作用域
    // 使用s
} // s离开作用域,内存自动被清理(调用drop)
# Python - 垃圾回收器管理
{
    s = "hello"  # 创建字符串对象
    # 使用s
}  # s超出作用域,但对象何时被清理由垃圾回收器决定

3.3 移动 (Move) 语义

Rust中的移动

// 移动语义示例
fn take_ownership(s: String) {
    println!("Got: {}", s);
    // s在函数结束时被清理
}

fn main() {
    let s = String::from("hello");
    take_ownership(s);           // s被移动到函数中
    // println!("{}", s);        // 编译错误:s已经被移动
}

Python中的引用传递

# Python中传递的是引用
def take_reference(s):
    print(f"Got: {s}")
    # 对象仍然存在于内存中

def main():
    s = "hello"
    take_reference(s)            # 传递引用
    print(s)                     # 正常:s仍然可用

main()

避免移动:Clone

// 使用Clone避免移动
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();         // 深拷贝,s1仍然可用
    
    println!("s1: {}", s1);      // 正确
    println!("s2: {}", s2);      // 正确
}
# Python中的浅拷贝和深拷贝
import copy

def main():
    s1 = ["hello", "world"]
    s2 = s1.copy()               # 浅拷贝
    s3 = copy.deepcopy(s1)       # 深拷贝
    
    print(f"s1: {s1}")
    print(f"s2: {s2}")
    print(f"s3: {s3}")

main()

3.4 借用 (Borrowing) 和引用

借用允许你使用值而不获取其所有权:

不可变借用

// Rust不可变借用
fn calculate_length(s: &String) -> usize {
    s.len()  // 可以读取,但不能修改
}

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // 借用s1的引用
    println!("'{}' has length {}", s1, len);  // s1仍然可用
}
# Python中函数参数传递对象引用
def calculate_length(s):
    return len(s)  # 可以读取

def main():
    s1 = "hello"
    length = calculate_length(s1)  # 传递引用
    print(f"'{s1}' has length {length}")  # s1仍然可用

main()

可变借用

// Rust可变借用
fn append_world(s: &mut String) {
    s.push_str(", world!");
}

fn main() {
    let mut s = String::from("hello");
    append_world(&mut s);           // 可变借用
    println!("{}", s);              // 输出:hello, world!
}
# Python中可变对象的修改
def append_world(s):
    s.append(", world!")  # 直接修改列表

def main():
    s = ["hello"]
    append_world(s)       # 传递引用,函数内可以修改
    print(s)              # 输出:['hello', ', world!']

main()

3.5 借用规则

Rust的借用有严格的规则来防止数据竞争:

  1. 可以有任意数量的不可变引用
  2. 只能有一个可变引用
  3. 不可变引用和可变引用不能同时存在

规则演示

// Rust借用规则
fn main() {
    let mut s = String::from("hello");
    
    // 多个不可变引用 - 正确
    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);
    
    // 可变引用 - 正确(不可变引用已经不再使用)
    let r3 = &mut s;
    r3.push_str(", world!");
    println!("{}", r3);
    
    // 以下会编译错误:
    // let r4 = &s;      // 不能在可变引用存在时创建不可变引用
    // let r5 = &mut s;  // 不能有两个可变引用
}
# Python没有这种借用限制
def main():
    s = ["hello"]
    
    # 可以有多个引用指向同一对象
    r1 = s
    r2 = s
    r3 = s
    
    # 所有引用都可以修改对象(如果对象是可变的)
    r1.append("world")
    r2.append("from")
    r3.append("Python")
    
    print(s)  # 输出:['hello', 'world', 'from', 'Python']

main()

3.6 生命周期 (Lifetimes)

生命周期确保引用在需要时一直有效:

基本生命周期概念

// Rust生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string is long");
    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
    // string2在这里已经被清理,但result不能引用它
}
# Python不需要显式的生命周期管理
def longest(x, y):
    if len(x) > len(y):
        return x
    else:
        return y

def main():
    string1 = "long string is long"
    
    # 这个作用域在Python中不重要
    string2 = "xyz"
    result = longest(string1, string2)
    print(f"The longest string is {result}")
    
    # 即使string2"超出作用域",在Python中对象仍然存在
    # 因为result仍然引用它

main()

3.7 切片 (Slices)

切片是对集合中一段连续元素的引用:

字符串切片

// Rust字符串切片
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]  // 返回整个字符串的切片
}

fn main() {
    let s = String::from("hello world");
    let word = first_word(&s);
    println!("First word: {}", word);
    
    // 字符串切片语法
    let hello = &s[0..5];   // "hello"
    let world = &s[6..11];  // "world"
    let full = &s[..];      // "hello world"
}
# Python字符串切片
def first_word(s):
    words = s.split(' ')
    return words[0] if words else ""

def main():
    s = "hello world"
    word = first_word(s)
    print(f"First word: {word}")
    
    # Python切片语法
    hello = s[0:5]    # "hello"
    world = s[6:11]   # "world"
    full = s[:]       # "hello world"

main()

数组切片

// Rust数组切片
fn main() {
    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];    // [2, 3]
    
    println!("Array: {:?}", a);
    println!("Slice: {:?}", slice);
    
    // 切片是借用,不拥有数据
    for element in slice {
        println!("Element: {}", element);
    }
}
# Python列表切片
def main():
    a = [1, 2, 3, 4, 5]
    slice_list = a[1:3]      # [2, 3]
    
    print(f"List: {a}")
    print(f"Slice: {slice_list}")
    
    # Python切片创建新列表
    for element in slice_list:
        print(f"Element: {element}")

main()

3.8 所有权与函数

函数参数的所有权转移

// Rust函数所有权示例
fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string离开作用域并被drop

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer离开作用域,但i32是Copy类型,所以原值仍然有效

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    some_string  // 返回some_string,将所有权转移给调用者
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string  // a_string被返回,所有权转移给调用者
}

fn main() {
    let s = gives_ownership();         // gives_ownership将所有权转移给s
    
    let s2 = String::from("hello");    // s2进入作用域
    let s3 = takes_and_gives_back(s2); // s2被移动到函数中,函数返回值移动给s3
    
    // println!("{}", s2);             // 错误:s2的值已经被移动
    println!("{}", s3);                // 正确:s3拥有所有权
    
    let x = 5;                         // x进入作用域
    makes_copy(x);                     // x被传递给函数,但i32是Copy类型
    println!("{}", x);                 // 正确:x仍然有效
}
# Python函数参数传递
def takes_reference(some_string):
    print(some_string)
    # 对象仍然存在,只是引用被传递

def makes_copy(some_integer):
    print(some_integer)
    # 整数是不可变的,实际上是传递值

def gives_reference():
    some_string = "hello"
    return some_string  # 返回对字符串的引用

def takes_and_gives_back(a_string):
    return a_string     # 返回相同的引用

def main():
    s = gives_reference()              # s获得对字符串的引用
    
    s2 = "hello"                       # s2引用字符串
    s3 = takes_and_gives_back(s2)      # s2仍然有效,s3引用相同字符串
    
    print(s2)                          # 正确:s2仍然有效
    print(s3)                          # 正确:s3引用字符串
    
    x = 5                              # x绑定到整数5
    makes_copy(x)                      # 传递整数值
    print(x)                           # 正确:x仍然有效

main()

3.9 引用与借用的高级用法

引用的引用

// Rust引用的引用
fn main() {
    let s = String::from("hello");
    let r1 = &s;              // &String
    let r2 = &r1;             // &&String
    
    println!("s: {}", s);
    println!("r1: {}", r1);
    println!("r2: {}", r2);   // 自动解引用
}
# Python中所有的都是引用
def main():
    s = "hello"
    r1 = s                    # 引用同一个字符串对象
    r2 = r1                   # 引用同一个字符串对象
    
    print(f"s: {s}")
    print(f"r1: {r1}")
    print(f"r2: {r2}")

main()

解引用操作符

// Rust解引用
fn main() {
    let x = 5;
    let y = &x;               // y是x的引用
    
    assert_eq!(5, x);
    assert_eq!(5, *y);        // 解引用y来获取值
    
    // 以下会编译错误:
    // assert_eq!(5, y);      // 不能比较i32和&i32
}
# Python不需要显式解引用
def main():
    x = 5
    y = x                     # y引用相同的整数对象
    
    assert x == 5
    assert y == 5             # 直接比较,不需要解引用
    
    # Python中变量总是引用,不需要显式解引用操作

main()

3.10 Copy trait 和 Clone trait

Copy trait(自动拷贝)

// Copy trait - 栈上数据的按位复制
fn main() {
    let x = 5;                // i32实现了Copy
    let y = x;                // x被复制到y,x仍然有效
    
    println!("x: {}, y: {}", x, y);  // 都可以使用
    
    // 实现Copy的类型:
    // - 所有整数类型(i32, u32等)
    // - 布尔类型(bool)
    // - 所有浮点类型(f32, f64)
    // - 字符类型(char)
    // - 仅包含Copy类型的元组
}

Clone trait(显式拷贝)

// Clone trait - 可能开销较大的深拷贝
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();      // 显式拷贝
    
    println!("s1: {}, s2: {}", s1, s2);  // 都可以使用
    
    // 自定义类型的Clone实现
    #[derive(Clone)]
    struct Point {
        x: i32,
        y: i32,
    }
    
    let p1 = Point { x: 1, y: 2 };
    let p2 = p1.clone();      // 克隆Point
    
    println!("p1: ({}, {})", p1.x, p1.y);
    println!("p2: ({}, {})", p2.x, p2.y);
}
# Python中的浅拷贝和深拷贝
import copy

def main():
    # 不可变对象(整数、字符串)的"拷贝"
    x = 5
    y = x                     # 实际上引用同一对象(因为整数不可变)
    print(f"x: {x}, y: {y}")
    
    # 可变对象的拷贝
    s1 = ["hello"]
    s2 = s1.copy()            # 浅拷贝
    s3 = copy.deepcopy(s1)    # 深拷贝
    
    print(f"s1: {s1}, s2: {s2}, s3: {s3}")
    
    # 自定义类的拷贝
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
    p1 = Point(1, 2)
    p2 = copy.copy(p1)        # 浅拷贝
    p3 = copy.deepcopy(p1)    # 深拷贝
    
    print(f"p1: ({p1.x}, {p1.y})")
    print(f"p2: ({p2.x}, {p2.y})")
    print(f"p3: ({p3.x}, {p3.y})")

main()

3.11 实践练习

练习1:所有权和借用

// Rust练习:字符串处理
fn process_string(s: &str) -> String {
    let mut result = String::new();
    for ch in s.chars() {
        if ch.is_alphabetic() {
            result.push(ch.to_uppercase().next().unwrap());
        }
    }
    result
}

fn main() {
    let original = String::from("hello, world! 123");
    let processed = process_string(&original);  // 借用
    
    println!("Original: {}", original);         // original仍然可用
    println!("Processed: {}", processed);
    
    // 练习:修改函数以接受String而不是&str
    fn take_ownership(s: String) -> String {
        s.to_uppercase()
    }
    
    let another = String::from("rust programming");
    let result = take_ownership(another);
    // println!("{}", another);  // 错误:another已被移动
    println!("Result: {}", result);
}
# Python练习:字符串处理
def process_string(s):
    result = ""
    for ch in s:
        if ch.isalpha():
            result += ch.upper()
    return result

def main():
    original = "hello, world! 123"
    processed = process_string(original)        # 传递引用
    
    print(f"Original: {original}")              # original仍然可用
    print(f"Processed: {processed}")
    
    # Python版本不需要考虑所有权
    def take_reference(s):
        return s.upper()
    
    another = "rust programming"
    result = take_reference(another)
    print(another)                              # another仍然可用
    print(f"Result: {result}")

main()

练习2:Vector操作

// Rust练习:Vector所有权
fn analyze_numbers(numbers: &[i32]) -> (i32, i32, f64) {
    let min = *numbers.iter().min().unwrap();
    let max = *numbers.iter().max().unwrap();
    let avg = numbers.iter().sum::<i32>() as f64 / numbers.len() as f64;
    (min, max, avg)
}

fn take_and_modify(mut numbers: Vec<i32>) -> Vec<i32> {
    numbers.push(100);
    numbers.sort();
    numbers
}

fn main() {
    let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6];
    
    // 借用进行分析
    let (min, max, avg) = analyze_numbers(&numbers);
    println!("Min: {}, Max: {}, Average: {:.2}", min, max, avg);
    println!("Original: {:?}", numbers);  // numbers仍然可用
    
    // 转移所有权进行修改
    let modified = take_and_modify(numbers);
    // println!("{:?}", numbers);         // 错误:numbers已被移动
    println!("Modified: {:?}", modified);
}
# Python练习:列表操作
def analyze_numbers(numbers):
    min_val = min(numbers)
    max_val = max(numbers)
    avg = sum(numbers) / len(numbers)
    return min_val, max_val, avg

def take_and_modify(numbers):
    numbers = numbers.copy()  # 手动拷贝以避免修改原列表
    numbers.append(100)
    numbers.sort()
    return numbers

def main():
    numbers = [3, 1, 4, 1, 5, 9, 2, 6]
    
    # 传递引用进行分析
    min_val, max_val, avg = analyze_numbers(numbers)
    print(f"Min: {min_val}, Max: {max_val}, Average: {avg:.2f}")
    print(f"Original: {numbers}")         # numbers仍然可用
    
    # Python中需要手动决定是否拷贝
    modified = take_and_modify(numbers)
    print(f"Original after: {numbers}")   # numbers未被修改
    print(f"Modified: {modified}")

main()

3.12 所有权系统的优势

内存安全

// Rust在编译时防止内存错误
fn rust_memory_safety() {
    let s = String::from("hello");
    let r = &s;
    
    // drop(s);              // 编译错误:不能在有借用时释放
    println!("{}", r);       // 安全:借用仍然有效
}
# Python在运行时可能出现问题
import weakref

def python_memory_issues():
    class MyClass:
        def __init__(self, value):
            self.value = value
    
    obj = MyClass("hello")
    weak_ref = weakref.ref(obj)
    
    del obj                  # 删除强引用
    
    if weak_ref() is not None:
        print(weak_ref().value)  # 可能出错:对象可能已被回收
    else:
        print("Object was garbage collected")

零成本抽象

// Rust的所有权检查发生在编译时,运行时无额外开销
fn zero_cost_abstractions() {
    let v = vec![1, 2, 3, 4, 5];
    
    // 这些借用在运行时没有额外开销
    let slice1 = &v[0..2];
    let slice2 = &v[2..];
    
    println!("Slice1: {:?}", slice1);
    println!("Slice2: {:?}", slice2);
}

3.13 常见错误和解决方案

错误1:使用已移动的值

// 常见错误
fn common_mistake() {
    let s = String::from("hello");
    let s2 = s;                    // s被移动
    // println!("{}", s);          // 编译错误
}

// 解决方案1:克隆
fn solution1() {
    let s = String::from("hello");
    let s2 = s.clone();            // 克隆而不是移动
    println!("s: {}, s2: {}", s, s2);
}

// 解决方案2:借用
fn solution2() {
    let s = String::from("hello");
    let s2 = &s;                   // 借用而不是移动
    println!("s: {}, s2: {}", s, s2);
}

错误2:生命周期不匹配

// 常见错误
fn lifetime_error() -> &str {
    let s = String::from("hello");
    // &s[..]                      // 编译错误:返回局部变量的引用
    "hello"  // 字符串字面量有'static生命周期,所以这样是可以的
}

// 解决方案:返回拥有所有权的值
fn lifetime_solution() -> String {
    let s = String::from("hello");
    s                              // 返回所有权
}

3.14 本章小结

通过本章学习,你应该掌握:

  1. 所有权概念:每个值有唯一所有者,所有者离开作用域时值被清理
  2. 移动语义:值的所有权可以转移,原所有者不再可用
  3. 借用系统:可以借用值的引用而不获取所有权
  4. 生命周期:确保引用在需要时始终有效
  5. Copy和Clone:理解何时发生拷贝,何时需要显式克隆

与Python的关键差异:

特性RustPython
内存管理编译时所有权系统运行时垃圾回收
变量赋值可能移动或拷贝总是引用赋值
函数参数所有权转移或借用引用传递
内存安全编译时保证运行时检查
性能开销零运行时开销垃圾回收开销

关键要点:

  • 所有权系统在编译时就能防止内存错误
  • 借用允许安全地共享数据而不转移所有权
  • 理解何时使用移动、借用或克隆是关键
  • 生命周期确保引用的安全性

下一章预告: 我们将学习Rust的控制流和函数,包括模式匹配等强大特性。