Rust 入门实战
Rust 是一门强调安全性和性能的系统编程语言,通过所有权机制在编译时消除内存错误,不需要垃圾回收就能保证内存安全,是编写高性能生信工具的理想选择。
核心知识点
| 知识点 | 说明 |
|---|
| 语言类型 | 系统编程语言,编译型 |
| 最新版本 | Rust 1.90+(2024 Edition) |
| 核心优势 | 内存安全(无 GC)、高性能、零成本抽象 |
| 包管理器 | Cargo(构建 + 依赖管理 + 测试) |
| 适用场景 | CLI 工具、高性能计算、WebAssembly、系统工具 |
| 生信应用 | 很多新一代生信工具用 Rust 写(如 nf-core 组件) |
| 学习资源 | "The Book"(官方教程)、Rustlings(练习题) |
安装配置
# 安装 Rust 工具链(rustup)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装 rustup
source $HOME/.cargo/env # 加载环境变量
# 验证安装
rustc --version # Rust 编译器版本
cargo --version # Cargo 包管理器版本
# 更新 Rust
rustup update # 更新到最新版本
# 安装常用组件
rustup component add rustfmt # 代码格式化工具
rustup component add clippy # 代码检查工具
基本使用
1. 创建项目
cargo new hello_rust # 创建新项目
cd hello_rust # 进入项目目录
cargo run # 编译并运行
2. 基本语法
// src/main.rs
fn main() {
// 变量(默认不可变)
let name = "Rust"; // 不可变变量
let mut count = 0; // 可变变量(mut 关键字)
count += 1;
// 数据类型
let age: u32 = 25; // 无符号 32 位整数
let bmi: f64 = 28.5; // 64 位浮点数
let is_healthy: bool = false; // 布尔值
let sample_id: &str = "T2D_001"; // 字符串切片
// 打印
println!("Hello, {}!", name); // 格式化打印
println!("BMI: {:.2}", bmi); // 保留两位小数
// if 表达式
let status = if bmi > 25.0 { // if 是表达式,有返回值
"overweight"
} else {
"normal"
};
// 循环
for i in 0..5 { // 范围循环 0,1,2,3,4
println!("第 {} 次", i);
}
// 向量(动态数组)
let mut scores: Vec<f64> = vec![8.5, 9.0, 7.5]; // 创建向量
scores.push(8.0); // 添加元素
let avg: f64 = scores.iter().sum::<f64>() / scores.len() as f64; // 计算平均值
println!("平均分: {:.1}", avg);
}
3. 所有权系统(Rust 核心概念)
fn main() {
// 所有权规则:
// 1. 每个值有且只有一个所有者
// 2. 同一时间只能有一个所有者
// 3. 所有者离开作用域,值被释放
let s1 = String::from("hello"); // s1 拥有这个字符串
let s2 = s1; // 所有权转移给 s2,s1 不再可用
// println!("{}", s1); // 编译错误!s1 已经失效
// 借用(引用)—— 不转移所有权
let s3 = String::from("world");
let len = calculate_length(&s3); // & 表示借用,不转移所有权
println!("长度: {}, 内容: {}", len, s3); // s3 仍然可用
// 可变借用
let mut s4 = String::from("hello");
change(&mut s4); // &mut 可变借用
println!("{}", s4); // 输出 "hello, world"
}
fn calculate_length(s: &String) -> usize { // 接受引用,不获取所有权
s.len()
}
fn change(s: &mut String) { // 接受可变引用
s.push_str(", world");
}
4. 结构体和枚举
// 定义结构体
struct Sample {
id: String, // 样本 ID
diagnosis: String, // 诊断
bmi: f64, // BMI
}
impl Sample { // 为结构体实现方法
fn new(id: &str, diagnosis: &str, bmi: f64) -> Self { // 构造函数
Sample {
id: id.to_string(),
diagnosis: diagnosis.to_string(),
bmi,
}
}
fn is_overweight(&self) -> bool { // 方法
self.bmi > 25.0
}
}
// 枚举(Rust 的枚举非常强大)
enum DiagnosisGroup {
T2D,
Healthy,
Unknown(String), // 可以携带数据
}
fn main() {
let s = Sample::new("T2D_001", "T2D", 28.5);
println!("{} 超重: {}", s.id, s.is_overweight());
// 模式匹配(match)
let group = DiagnosisGroup::T2D;
match group {
DiagnosisGroup::T2D => println!("2型糖尿病"),
DiagnosisGroup::Healthy => println!("健康对照"),
DiagnosisGroup::Unknown(msg) => println!("未知: {}", msg),
}
}
高级用法
1. 错误处理(Result 类型)
use std::fs; // 文件系统模块
use std::io;
fn read_sample_data(path: &str) -> Result<String, io::Error> {
let content = fs::read_to_string(path)?; // ? 操作符:出错则返回错误
Ok(content) // 成功返回内容
}
fn main() {
match read_sample_data("data.txt") {
Ok(data) => println!("读取成功: {} 字节", data.len()),
Err(e) => eprintln!("读取失败: {}", e),
}
}
2. 命令行工具(使用 clap)
# Cargo.toml 添加依赖
[dependencies]
clap = { version = "4", features = ["derive"] }
use clap::Parser; // 导入 clap 的 Parser
#[derive(Parser)]
#[command(name = "bmi_calc", about = "BMI 计算器")]
struct Args {
#[arg(short, long)]
weight: f64, // 体重(kg)
#[arg(short = 'H', long)]
height: f64, // 身高(m)
}
fn main() {
let args = Args::parse(); // 自动解析命令行参数
let bmi = args.weight / (args.height * args.height);
println!("BMI: {:.1}", bmi);
}
// 运行: cargo run -- --weight 75 --height 1.75
3. 文件读写(处理 TSV/CSV)
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 读取 TSV 文件
let file = File::open("abundance.tsv")?;
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line?;
let fields: Vec<&str> = line.split('\t').collect(); // 按 tab 分割
if fields.len() >= 2 {
println!("样本: {}, 丰度: {}", fields[0], fields[1]);
}
}
// 写入文件
let mut output = File::create("output.tsv")?;
writeln!(output, "sample\tbmi\tgroup")?;
writeln!(output, "T2D_001\t28.5\tcase")?;
Ok(())
}
常见报错与解决
| 报错信息 | 原因 | 解决方法 |
|---|
value used after move | 所有权已转移 | 使用 .clone() 或借用 & |
cannot borrow as mutable | 不可变变量尝试修改 | 加 mut 关键字 |
lifetime error | 生命周期不匹配 | 添加生命周期标注或重构 |
type mismatch | 类型不匹配 | 使用 as 转换或 into() |
unresolved import | 依赖未添加 | 在 Cargo.toml 中添加依赖 |
速查表
// ===== Rust 速查表 =====
// 变量
let x = 5; // 不可变
let mut y = 10; // 可变
let z: f64 = 3.14; // 指定类型
// 函数
fn add(a: i32, b: i32) -> i32 { a + b }
// 控制流
if x > 0 { } else { }
for i in 0..10 { }
while x > 0 { }
loop { break; }
// 集合
let v = vec![1, 2, 3]; // 向量
let mut m = HashMap::new(); // 哈希表
// 错误处理
let r: Result<T, E> = Ok(val);
let o: Option<T> = Some(val);
let val = result?; // 传播错误
// Cargo 命令
// cargo new project 创建项目
// cargo build 编译
// cargo run 编译运行
// cargo test 运行测试
// cargo fmt 格式化代码
// cargo clippy 代码检查
// cargo add dep 添加依赖