当前位置:首页 > 学习笔记 > 正文内容

Rust 系统编程入门教程

廖万里10小时前学习笔记0
Rust 系统编程入门
Rust 是一门专注于安全、并发和高性能的系统编程语言。通过独特的所有权系统和零成本抽象,它在编译期就能保证内存安全,同时不牺牲运行效率。本教程将从语言特性、所有权系统、借用与生命周期、模块系统、错误处理、并发编程等核心概念入手,配合实战案例,帮助你快速入门 Rust 系统编程。
## 一、Rust 语言特性概述 Rust 由 Mozilla 研发,旨在解决系统编程中长期存在的内存安全问题。与 C/C++ 不同,Rust 通过编译器强制执行的借用检查器(Borrow Checker),在编译阶段就能杜绝空指针悬垂指针、数据竞争等常见内存错误。 Rust 的核心设计理念: - **内存安全无需 GC**:通过所有权系统实现自动内存管理 - **零成本抽象**:高级特性不会带来运行时开销 - **并发安全**:类型系统天然防止数据竞争 - **实用主义**:支持函数式、面向对象等多种编程范式 ## 二、所有权系统 所有权是 Rust 最独特也是最核心的概念。每个值都有一个所有者(owner),同一时刻只能有一个所有者。当所有者离开作用域时,值会被自动释放。 ### 所有权规则
fn ownership_demo() {
    // 规则1:每个值有且只有一个所有者
    let s1 = String::from("hello");
    let s2 = s1; // s1 的所有权转移给 s2,s1 不再有效
    
    // println!("{}", s1); // 编译错误!s1 已被移动
    
    // 规则2:所有者离开作用域时,值被自动释放
    {
        let s3 = String::from("world");
        println!("{}", s3); // 有效
    } // s3 在此处被 drop
    
    // println!("{}", s3); // 编译错误!s3 已被释放
    
    // 规则3:基本类型实现 Copy trait,赋值时会复制而非移动
    let x = 5;
    let y = x;
    println!("x = {}, y = {}", x, y); // 两者都有效
}
### 所有权与函数
fn take_ownership(s: String) {
    println!("Got: {}", s);
} // s 在这里被 drop

fn make_copy(i: i32) {
    println!("Got: {}", i);
} // i 只是复制,原始值不受影响

fn main() {
    let s = String::from("hello");
    take_ownership(s);
    // println!("{}", s); // 错误!s 已被移动
    
    let x = 42;
    make_copy(x);
    println!("x still valid: {}", x); // 正常!x 被复制
}
## 三、借用与生命周期 ### 引用与借用 如果每次传递数据都需要转移所有权,代码将变得非常繁琐。Rust 提供了引用机制,允许你借用值而不获取所有权。
fn calculate_length(s: &String) -> usize {
    s.len()
} // s 是引用,离开作用域不释放原值

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}", s1, len);
}
### 可变引用 默认引用是不可变的。如果需要修改借用的值,需要使用可变引用。
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
}
### 借用规则 Rust 对引用有严格限制: 1. **可以有多个不可变引用**,或者**一个可变引用**,但不能同时存在 2. 引用必须始终有效(不能悬垂)
fn main() {
    let mut s = String::from("hello");
    
    let r1 = &s;      // 正确
    let r2 = &s;      // 正确
    // let r3 = &mut s; // 错误!已有不可变引用
    
    // println!("{} {} {}", r1, r2, r3);
    
    // 正确做法:不可变引用使用完毕后再创建可变引用
    println!("{} {}", r1, r2);
    // r1, r2 此后不再使用
    
    let r3 = &mut s;  // 现在可以了
    r3.push_str(" world");
    println!("{}", r3);
}
### 生命周期标注 当引用作为函数返回值时,编译器需要知道引用的有效期。这时需要显式标注生命周期。
// 生命周期参数 'a 表示返回引用与参数 str 的生命周期相同
fn longest<'a>(str1: &'a str, str2: &'a str) -> &'a str {
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}

fn main() {
    let string1 = String::from("long string");
    let result;
    {
        let string2 = String::from("short");
        result = longest(string1.as_str(), string2.as_str());
        println!("Longest: {}", result);
    }
    // println!("{}", result); // 可能错误!如果 result 指向 string2
}
## 四、模块系统 Rust 的模块系统帮助组织代码,控制可见性。 ### 模块定义与使用
// src/lib.rs 或 src/main.rs
mod network {
    pub mod tcp {
        pub fn connect(addr: &str) {
            println!("Connecting to {}", addr);
        }
    }
    
    pub mod udp {
        pub fn bind(port: u16) {
            println!("Binding to port {}", port);
        }
    }
    
    // 私有函数,外部不可见
    fn internal_helper() {
        println!("Internal helper");
    }
}

fn main() {
    network::tcp::connect("127.0.0.1:8080");
    network::udp::bind(8080);
    // network::internal_helper(); // 错误!私有函数
}
### use 关键字
use std::collections::HashMap;
use std::io::{self, Read, Write}; // io 本身和其下的 Read, Write

fn main() -> io::Result<()> {
    let mut map: HashMap<&str, i32> = HashMap::new();
    map.insert("one", 1);
    map.insert("two", 2);
    
    println!("{:?}", map);
    Ok(())
}
## 五、错误处理 Rust 没有异常机制,而是使用 `Result` 和 `Option` 进行显式错误处理。 ### Result 类型
use std::fs::File;
use std::io::Read;

fn read_file_content(path: &str) -> Result {
    let mut file = File::open(path)?;  // ? 运算符自动传播错误
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}

fn main() {
    match read_file_content("data.txt") {
        Ok(content) => println!("Content: {}", content),
        Err(e) => eprintln!("Error: {}", e),
    }
}
### Option 类型
fn find_user(id: u32) -> Option {
    if id == 1 {
        Some(String::from("Alice"))
    } else if id == 2 {
        Some(String::from("Bob"))
    } else {
        None
    }
}

fn main() {
    // 使用 match
    match find_user(1) {
        Some(name) => println!("Found: {}", name),
        None => println!("Not found"),
    }
    
    // 使用 if let 语法糖
    if let Some(name) = find_user(2) {
        println!("Found: {}", name);
    }
    
    // 使用 unwrap_or_default
    let name = find_user(3).unwrap_or_default();
    println!("Name: '{}'", name);  // 输出空字符串
}
### 自定义错误类型
use std::fmt;
use std::error::Error;

#[derive(Debug)]
enum AppError {
    IoError(std::io::Error),
    ParseError(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::IoError(e) => write!(f, "IO Error: {}", e),
            AppError::ParseError(s) => write!(f, "Parse Error: {}", s),
        }
    }
}

impl Error for AppError {}

impl From for AppError {
    fn from(e: std::io::Error) -> Self {
        AppError::IoError(e)
    }
}

fn parse_config(content: &str) -> Result {
    content.trim().parse::()
        .map_err(|e| AppError::ParseError(e.to_string()))
}

fn main() -> Result<(), AppError> {
    let content = std::fs::read_to_string("config.txt")?;
    let value = parse_config(&content)?;
    println!("Config value: {}", value);
    Ok(())
}
## 六、并发编程 Rust 的类型系统天然防止数据竞争,让并发编程更加安全。 ### 线程创建
use std::thread;
use std::time::Duration;

fn main() {
    // 创建线程
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("Spawned thread: {}", i);
            thread::sleep(Duration::from_millis(100));
        }
    });

    // 主线程
    for i in 1..=3 {
        println!("Main thread: {}", i);
        thread::sleep(Duration::from_millis(100));
    }

    // 等待子线程完成
    handle.join().unwrap();
}
### 线程间通信
use std::thread;
use std::sync::mpsc;  // multiple producer, single consumer

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let messages = vec![
            String::from("Hello"),
            String::from("from"),
            String::from("thread"),
        ];
        
        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(100));
        }
    });

    // 主线程接收消息
    for received in rx {
        println!("Received: {}", received);
    }
}
### 共享状态与 Mutex
use std::thread;
use std::sync::{Arc, Mutex};

fn main() {
    // Arc: Atomic Reference Counting,多线程共享所有权
    // Mutex: 互斥锁,保证同一时刻只有一个线程访问数据
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final counter: {}", *counter.lock().unwrap());
}
### Send 和 Sync Trait Rust 通过 Send 和 Sync trait 在编译期保证并发安全: - `Send`:类型可以安全地在线程间转移所有权 - `Sync`:类型可以安全地被多个线程共享引用
use std::thread;
use std::sync::Arc;

// 所有基本类型都实现了 Send
// 大多数类型自动实现这两个 trait

fn is_send() {}
fn is_sync() {}

fn main() {
    is_send::();       // i32 是 Send
    is_sync::();       // i32 是 Sync
    is_send::();    // String 是 Send
    is_sync::>();  // Arc 是 Sync
}
## 七、实战案例:构建简单的 Web 服务器 让我们用 Rust 构建一个简单的 HTTP 服务器,综合运用前面学到的知识。
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::fs;
use std::thread;
use std::time::Duration;
use std::sync::Arc;

/// HTTP 服务器配置
struct ServerConfig {
    host: String,
    port: u16,
}

impl ServerConfig {
    fn new(host: &str, port: u16) -> Self {
        ServerConfig {
            host: host.to_string(),
            port,
        }
    }
    
    fn address(&self) -> String {
        format!("{}:{}", self.host, self.port)
    }
}

/// 处理客户端连接
fn handle_connection(mut stream: TcpStream) {
    let mut buffer = [0; 1024];
    
    match stream.read(&mut buffer) {
        Ok(_) => {
            let request = String::from_utf8_lossy(&buffer[..]);
            println!("Request:\n{}", request);
            
            let (status_line, filename) = if request.starts_with("GET / ") {
                ("HTTP/1.1 200 OK", "index.html")
            } else if request.starts_with("GET /sleep ") {
                thread::sleep(Duration::from_secs(5));
                ("HTTP/1.1 200 OK", "index.html")
            } else {
                ("HTTP/1.1 404 NOT FOUND", "404.html")
            };
            
            let contents = fs::read_to_string(filename).unwrap_or_default();
            let response = format!(
                "{}\r\nContent-Length: {}\r\n\r\n{}",
                status_line,
                contents.len(),
                contents
            );
            
            let _ = stream.write_all(response.as_bytes());
            let _ = stream.flush();
        }
        Err(e) => eprintln!("Failed to read from stream: {}", e),
    }
}

fn main() -> std::io::Result<()> {
    let config = Arc::new(ServerConfig::new("127.0.0.1", 7878));
    
    println!("Server starting at {}...", config.address());
    
    let listener = TcpListener::bind(config.address())?;
    
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(|| {
                    handle_connection(stream);
                });
            }
            Err(e) => eprintln!("Connection failed: {}", e),
        }
    }
    
    Ok(())
}
### 使用线程池优化
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

type Job = Box;

enum Message {
    NewJob(Job),
    Terminate,
}

struct Worker {
    id: usize,
    thread: Option>,
}

impl Worker {
    fn new(id: usize, receiver: Arc>>) -> Self {
        let thread = thread::spawn(move || loop {
            let message = receiver.lock().unwrap().recv().unwrap();
            
            match message {
                Message::NewJob(job) => {
                    println!("Worker {} executing job", id);
                    job();
                }
                Message::Terminate => {
                    println!("Worker {} terminating", id);
                    break;
                }
            }
        });
        
        Worker {
            id,
            thread: Some(thread),
        }
    }
}

pub struct ThreadPool {
    workers: Vec,
    sender: Option>,
}

impl ThreadPool {
    pub fn new(size: usize) -> Self {
        assert!(size > 0);
        
        let (sender, receiver) = mpsc::channel();
        let receiver = Arc::new(Mutex::new(receiver));
        
        let mut workers = Vec::with_capacity(size);
        
        for id in 0..size {
            workers.push(Worker::new(id, Arc::clone(&receiver)));
        }
        
        ThreadPool {
            workers,
            sender: Some(sender),
        }
    }
    
    pub fn execute(&self, f: F)
    where
        F: FnOnce() + Send + 'static,
    {
        let job = Box::new(f);
        self.sender.as_ref().unwrap().send(Message::NewJob(job)).unwrap();
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        for _ in &self.workers {
            self.sender.as_ref().unwrap().send(Message::Terminate).unwrap();
        }
        
        for worker in &mut self.workers {
            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
        }
    }
}
## 总结 Rust 作为一门现代系统编程语言,通过所有权系统、借用检查器等创新机制,在不牺牲性能的前提下实现了内存安全和并发安全。本文从以下几个方面进行了讲解: 1. **语言特性**:Rust 的设计哲学和核心优势 2. **所有权系统**:理解值的所有权转移和自动释放机制 3. **借用与生命周期**:掌握引用的使用规则和生命周期标注 4. **模块系统**:学会组织代码和控制可见性 5. **错误处理**:使用 Result 和 Option 进行显式错误处理 6. **并发编程**:安全地进行多线程编程 Rust 的学习曲线虽然陡峭,但一旦掌握了这些核心概念,你将能够编写出安全、高效、可维护的系统级程序。建议多动手实践,从小项目开始,逐步深入。Rust 官方提供的 Rustlings 练习题和《Rust 程序设计语言》都是非常好的学习资源。 记住:编译器是你的朋友,那些"烦人"的错误信息正在帮你避免运行时 bug。拥抱编译器,享受 Rust 带来的编程乐趣!

本文链接:https://www.kkkliao.cn/?id=959 转载需授权!

分享到:

版权声明:本文由廖万里的博客发布,如需转载请注明出处。


发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。