Rust 教程及示例:代码实践与学习 – wiki基地

Rust 教程及示例:代码实践与学习

Rust 是一门现代化的系统编程语言,以其安全性、性能和并发性而闻名。它特别适合于开发底层基础设施、嵌入式系统、WebAssembly 和高性能应用。与 C 和 C++ 相比,Rust 在编译时进行严格的内存安全检查,从而避免了空指针、数据竞争等常见错误。尽管学习曲线较为陡峭,但掌握 Rust 后,开发者可以构建出更加可靠、高效的软件。

本文将深入探讨 Rust 的关键概念,并通过丰富的代码示例,帮助你快速入门并掌握这门强大的语言。我们将从基础语法开始,逐步介绍所有权系统、借用检查器、Trait 和泛型等高级特性,最终构建一个简单的命令行程序,让你体验 Rust 的魅力。

第一部分:Rust 基础

  1. Hello, World!

    让我们从经典的 “Hello, World!” 程序开始:

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

    这个程序定义了一个 main 函数,这是 Rust 程序的入口点。println! 是一个宏,用于将文本输出到控制台。

  2. 变量和数据类型

    Rust 是一种静态类型语言,这意味着每个变量都必须在编译时确定其类型。Rust 提供了多种基本数据类型,包括:

    • 整数类型: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize
    • 浮点数类型: f32, f64
    • 布尔类型: bool (true 或 false)。
    • 字符类型: char (Unicode 字符)。

    “`rust
    fn main() {
    let x: i32 = 10; // 声明一个 i32 类型的变量 x,并赋值为 10
    let y = 20; // 类型推断:Rust 会自动推断 y 的类型为 i32
    let z = 3.14; // 类型推断:Rust 会自动推断 z 的类型为 f64
    let is_true: bool = true;

    println!("x = {}, y = {}, z = {}, is_true = {}", x, y, z, is_true);
    
    // 变量默认是不可变的,需要使用 mut 关键字声明可变变量
    let mut mutable_x = 5;
    mutable_x = 6;
    
    println!("mutable_x = {}", mutable_x);
    

    }
    “`

    注意: 在 Rust 中,变量默认是不可变的(immutable)。如果需要修改变量的值,必须使用 mut 关键字声明可变变量。

  3. 函数

    函数使用 fn 关键字定义。函数可以接受参数并返回值。

    “`rust
    fn add(x: i32, y: i32) -> i32 {
    x + y // Rust 中不需要使用 return 关键字,最后一个表达式的值会自动作为返回值
    }

    fn main() {
    let result = add(5, 3);
    println!(“5 + 3 = {}”, result);
    }
    “`

  4. 控制流

    Rust 提供了 if, else, loop, whilefor 等控制流结构。

    “`rust
    fn main() {
    let number = 5;

    if number > 0 {
        println!("Number is positive");
    } else if number < 0 {
        println!("Number is negative");
    } else {
        println!("Number is zero");
    }
    
    let mut counter = 0;
    loop {
        counter += 1;
        println!("Counter = {}", counter);
        if counter == 5 {
            break; // 退出循环
        }
    }
    
    let mut i = 0;
    while i < 5 {
        println!("i = {}", i);
        i += 1;
    }
    
    let numbers = [1, 2, 3, 4, 5];
    for number in numbers.iter() {
        println!("Number in array: {}", number);
    }
    

    }
    “`

  5. 所有权、借用和生命周期

    所有权是 Rust 最核心的概念,它保证了内存安全,避免了数据竞争。所有权规则如下:

    • 每个值都有一个所有者(变量)。
    • 一次只能有一个所有者。
    • 当所有者离开作用域时,值将被丢弃。

    借用允许你访问数据而不转移所有权。Rust 使用借用检查器来确保借用规则的安全性。借用规则如下:

    • 你可以拥有一个可变借用,或者多个不可变借用,但不能同时拥有一个可变借用和任何不可变借用。
    • 借用必须在所有者有效时有效。

    生命周期是 Rust 用来确保借用有效性的另一种机制。编译器会根据变量的作用域推断生命周期。

    “`rust
    fn main() {
    let s1 = String::from(“hello”); // s1 获取了 “hello” 的所有权

    let s2 = s1; // 所有权转移给 s2,s1 不再有效
    // println!("{}", s1); // 错误!s1 已经被移动
    
    let s3 = String::from("world");
    let s4 = s3.clone(); // 克隆 s3,s3 和 s4 都拥有 "world" 的副本
    
    println!("s4 = {}", s4); // 正确!s4 仍然有效
    println!("s3 = {}", s3); // 正确!s3 仍然有效
    
    // 借用
    let s5 = String::from("rust");
    let len = calculate_length(&s5); // 借用 s5,而不是转移所有权
    println!("Length of '{}' is {}", s5, len);
    
    // 可变借用
    let mut s6 = String::from("mutable");
    change(&mut s6); // 可变借用 s6
    println!("s6 after change: {}", s6);
    

    }

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

    fn change(s: &mut String) {
    s.push_str(“, world”);
    }
    “`

    关键点:

    • String::from() 创建了一个堆上分配的字符串。
    • clone() 复制了字符串数据,创建了新的所有者。
    • & 创建了一个不可变借用。
    • &mut 创建了一个可变借用。

第二部分:高级 Rust 特性

  1. 结构体 (Structs)

    结构体用于将相关的数据组合在一起。

    “`rust
    struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
    }

    fn main() {
    let user1 = User {
    email: String::from(“[email protected]”),
    username: String::from(“username123”),
    active: true,
    sign_in_count: 1,
    };

    println!("User email: {}", user1.email);
    
    // 创建新用户,基于现有用户
    let user2 = User {
        email: String::from("[email protected]"),
        username: String::from("anotheruser"),
        ..user1 // 剩余字段使用 user1 的值
    };
    
    println!("User2 username: {}, active: {}", user2.username, user2.active);
    

    }
    “`

  2. 枚举 (Enums)

    枚举允许你定义一个类型,它可以是几种不同的值之一。

    “`rust
    enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
    }

    fn main() {
    let quit_message = Message::Quit;
    let move_message = Message::Move { x: 10, y: 20 };
    let write_message = Message::Write(String::from(“Hello, Enum!”));

    match move_message {
        Message::Quit => println!("Quit"),
        Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
        Message::Write(text) => println!("Write: {}", text),
        Message::ChangeColor(r, g, b) => println!("Change color to R: {}, G: {}, B: {}", r, g, b),
    }
    

    }
    “`

  3. Trait

    Trait 类似于其他语言中的接口。它们定义了一组方法,类型可以实现这些方法。

    “`rust
    trait Summary {
    fn summarize(&self) -> String;
    }

    struct NewsArticle {
    headline: String,
    location: String,
    author: String,
    content: String,
    }

    impl Summary for NewsArticle {
    fn summarize(&self) -> String {
    format!(“{}, by {} ({})”, self.headline, self.author, self.location)
    }
    }

    struct Tweet {
    username: String,
    content: String,
    reply: bool,
    retweet: bool,
    }

    impl Summary for Tweet {
    fn summarize(&self) -> String {
    format!(“{}: {}”, self.username, self.content)
    }
    }

    fn main() {
    let article = NewsArticle {
    headline: String::from(“Penguins win the Stanley Cup Championship!”),
    location: String::from(“Pittsburgh, PA, USA”),
    author: String::from(“Iceburgh”),
    content: String::from(“The Pittsburgh Penguins once again are the best hockey team in the NHL.”),
    };

    let tweet = Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false,
    };
    
    println!("News Article Summary: {}", article.summarize());
    println!("Tweet Summary: {}", tweet.summarize());
    

    }
    “`

  4. 泛型 (Generics)

    泛型允许你编写可以处理多种类型的代码。

    “`rust
    fn largest(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }
    
    largest
    

    }

    fn main() {
    let number_list = vec![34, 50, 25, 100, 65];
    let result = largest(&number_list);
    println!(“The largest number is {}”, result);

    let char_list = vec!['y', 'm', 'a', 'q'];
    let result = largest(&char_list);
    println!("The largest char is {}", result);
    

    }
    “`

    关键点:

    • T: PartialOrd + Copy 定义了泛型类型 T 需要实现 PartialOrd(可比较)和 Copy(可复制)这两个 trait。
  5. 模块 (Modules)

    模块允许你组织代码并控制可见性。

    “`rust
    mod front_of_house {
    pub mod hosting {
    pub fn add_to_waitlist() {}

        fn seat_at_table() {}
    }
    
    mod serving {
        fn take_order() {}
    
        fn serve_order() {}
    
        fn take_payment() {}
    }
    

    }

    pub fn eat_at_restaurant() {
    // Absolute path
    crate::front_of_house::hosting::add_to_waitlist();

    // Relative path
    front_of_house::hosting::add_to_waitlist();
    

    }

    fn main() {
    eat_at_restaurant();
    }
    “`

    关键点:

    • mod 关键字用于定义模块。
    • pub 关键字用于声明公共项 (public items),可以在模块外部访问。

第三部分:构建一个简单的命令行程序

让我们构建一个简单的命令行程序,它可以读取文件内容并统计单词的出现次数。

“`rust
use std::collections::HashMap;
use std::fs;
use std::io;
use std::io::Read;

fn main() -> io::Result<()> {
println!(“Enter the file path:”);

let mut filepath = String::new();
io::stdin().read_line(&mut filepath)?;
let filepath = filepath.trim();

let mut file = fs::File::open(filepath)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;

let mut word_counts = HashMap::new();
for word in contents.split_whitespace() {
    let count = word_counts.entry(word.to_string()).or_insert(0);
    *count += 1;
}

println!("Word counts:");
for (word, count) in &word_counts {
    println!("{}: {}", word, count);
}

Ok(())

}
“`

代码解释:

  1. 导入必要的模块: std::collections::HashMap, std::fs, std::io, std::io::Read
  2. 获取用户输入的文件路径: 使用 io::stdin().read_line() 读取用户输入。
  3. 打开文件: 使用 fs::File::open() 打开文件。
  4. 读取文件内容: 使用 file.read_to_string() 将文件内容读取到字符串 contents 中。
  5. 统计单词出现次数:
    • 创建一个 HashMap 来存储单词和它们的计数。
    • 使用 contents.split_whitespace() 将字符串拆分为单词。
    • 对于每个单词,使用 word_counts.entry(word.to_string()).or_insert(0) 获取或插入计数器。
    • 增加计数器的值。
  6. 打印单词计数: 遍历 HashMap 并打印每个单词及其计数。
  7. 错误处理: 使用 io::Result<()> 作为 main 函数的返回类型,并使用 ? 运算符处理错误。

编译和运行:

  1. 将代码保存为 word_count.rs
  2. 使用 rustc word_count.rs 编译代码。
  3. 运行可执行文件:./word_count
  4. 输入要统计单词的文件的路径。

总结

通过本文的学习,你已经掌握了 Rust 的基本语法、核心概念和一些高级特性。你还构建了一个简单的命令行程序,体验了 Rust 的实际应用。当然,这只是 Rust 世界的冰山一角。要深入学习 Rust,还需要不断地实践、阅读文档和参与社区。希望本文能够帮助你踏上 Rust 学习之旅,并最终成为一名优秀的 Rust 开发者! 继续探索 Rust 的强大功能,如异步编程、WebAssembly 开发等,你将会发现更多惊喜。 祝你学习愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部