Rust返回值、错误处理、包和模块和格式化输出

目录认识生命周期返回值与错误处理panic深入剖析可恢复的错误Result包和模块包Crate模块Module使用use及受限可见性注释和文档格式化输出认识生命周期什么是生命周期?定义:生命周期是Rust中用来管理引用的有效范围的概念。作用:确保引

目录


认识生命周期

什么是生命周期?

  • 定义:生命周期是 Rust 中用来管理引用的有效范围的概念。
  • 作用:确保引用不会超出被引用的数据的有效范围,避免悬空指针问题。

生命周期注解

  • 语法:使用 'a 形式的生命周期注解。
// 定义一个带有生命周期注解的函数
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("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

多个生命周期

多个生命周期注解:当函数或结构体涉及多个引用时,需要明确指定每个引用的生命周期。

// 定义一个带有多个生命周期注解的函数
fn longest_with_an_announcement<'a, 'b>(x: &'a str, y: &'a str, ann: &'b str) -> &'a str {
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("hello");
    let string2 = String::from("world");
    let result = longest_with_an_announcement(string1.as_str(), string2.as_str(), "Drum roll please");

    println!("The longest string is {}", result);
}

返回值与错误处理

错误处理概述

  • 不可恢复的错误:通常使用 panic! 宏处理。
  • 可恢复的错误:通常使用 Result 枚举处理。

panic! 深入剖析

panic! 宏

  • 定义:panic! 宏用于触发程序崩溃,并打印一条错误消息。
  • 用途:用于处理无法恢复的严重错误。
fn divide_by_zero() {
    panic!("Attempted to divide by zero");
}

fn main() {
    divide_by_zero();
}

panic! 的工作机制

  • 堆栈回溯:当 panic! 触发时,Rust 会打印堆栈回溯信息,帮助调试。
  • 资源清理:Rust 会在 panic! 发生时尝试清理资源,以防止内存泄漏。
fn main() {
    let v = vec![1, 2, 3];

    // 尝试访问越界的元素
    let result = v.get(5).expect("Index out of bounds");

    println!("Result: {:?}", result);
}

可恢复的错误 Result

Result 枚举

  • 定义:Result 枚举有两种变体:Ok(T) 和 Err(E)。
  • 用途:用于处理可以恢复的错误。

Result 的使用

  • 创建 Result:使用 Ok 和 Err 构造函数。
  • 匹配 Result:使用模式匹配或 match 表达式。
fn divide(a: isize, b: isize) -> Result<isize, &'static str> {
    if b == 0 {
        Err("Cannot divide by zero")
    } else {
        Ok(a / b)
    }
}

fn main() {
    let result = divide(10, 2);
    match result {
        Ok(value) => println!("Result: {}", value),
        Err(e) => println!("Error: {}", e),
    }
}

unwrap 和 expect

  • unwrap:用于从 Result 中提取值,如果结果是 Err,则触发 panic!。
  • expect:与 unwrap 类似,但可以提供自定义的错误消息。
fn main() {
    let result = divide(10, 0);
    let value = result.unwrap_or_else(|e| {
        println!("Error: {}", e);
        0
    });

    println!("Result: {}", value);
}

map 和 and_then

  • map:用于在 Ok 值上应用函数。
  • and_then:用于在 Ok 值上应用另一个 Result 函数。
fn square(x: isize) -> isize {
    x * x
}

fn main() {
    let result = divide(10, 2);
    let squared_result = result.map(square);

    match squared_result {
        Ok(value) => println!("Squared result: {}", value),
        Err(e) => println!("Error: {}", e),
    }
}

? 操作符

  • ? 操作符:用于从函数返回 Result,并自动处理 Err 情况。
fn divide(a: isize, b: isize) -> Result<isize, &'static str> {
    if b == 0 {
        Err("Cannot divide by zero")
    } else {
        Ok(a / b)
    }
}

fn calculate(a: isize, b: isize) -> Result<isize, &'static str> {
    let result = divide(a, b)?;
    Ok(result * 2)
}

fn main() {
    match calculate(10, 2) {
        Ok(value) => println!("Result: {}", value),
        Err(e) => println!("Error: {}", e),
    }
}

包和模块

包(Crate)

什么是包(Crate)

  • 定义:在 Rust 中,一个 crate 是一个编译单元,它可以是一个库(library)或者一个可执行程序(executable)。
  • 文件结构:一个 crate 通常包含一个 Cargo.toml 文件和一个入口文件(如 src/main.rssrc/lib.rs)。

创建一个简单的 crate

cargo new my_crate --bin
cd my_crate

Cargo.toml 文件

[package]
name = "my_crate"
version = "0.1.0"
authors = ["Your Name <your.email@example.com>"]
edition = "2018"

[dependencies]

入口文件

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

模块(Module)

什么是模块(Module)

  • 定义:模块是 Rust 中组织代码的基本单位,可以包含其他模块和项(如函数、结构体、枚举等)。
  • 语法:使用 mod 关键字定义模块。

创建模块

// src/main.rs
mod greeting;

fn main() {
    greeting::say_hello();
}

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

模块路径

  • 相对路径:使用 mod 关键字定义模块时,可以使用相对路径。
  • 绝对路径:从 crate 根目录开始的路径。
// src/main.rs
mod greeting;

fn main() {
    greeting::say_hello();
}

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

使用 use 关键字

引入模块

  • 引入模块:使用 use 关键字引入模块中的项。
// src/main.rs
mod greeting;

use greeting::say_hello;

fn main() {
    say_hello();
}

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

别名

  • 别名:可以为引入的项定义别名。
// src/main.rs
mod greeting;

use greeting::say_hello as greet;

fn main() {
    greet();
}

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

受限可见性

可见性修饰符

  • 私有(private):默认情况下,所有项都是私有的。
  • 公共(public):使用 pub 关键字声明公共项。
// src/main.rs
mod greeting;

use greeting::say_hello;

fn main() {
    say_hello();
}

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

可见性层次

  • 模块层次:可以控制整个模块的可见性。
  • 项层次:可以控制单个项的可见性。
// src/main.rs
mod greeting;

use greeting::say_hello;

fn main() {
    say_hello();
}

// src/greeting.rs
pub mod greeting {
    pub fn say_hello() {
        println!("Hello, world!");
    }
}

注释和文档

文档注释

  • 文档注释:使用 ////*! 开头的注释。
// src/main.rs
mod greeting;

use greeting::say_hello;

fn main() {
    say_hello();
}

// src/greeting.rs
/// Prints a greeting message.
pub fn say_hello() {
    println!("Hello, world!");
}

自动生成文档

  • cargo doc:可以生成文档。
cargo doc --open

格式化输出

format! 宏

格式化字符串:使用 format! 宏生成格式化的字符串。

fn main() {
    let name = "Alice";
    let age = 30;

    let message = format!("Hello, my name is {} and I am {} years old.", name, age);
    println!("{}", message);
}

writeln! 宏

  • 格式化输出到标准输出:使用 writeln! 宏输出格式化的字符串。
fn main() {
    let name = "Alice";
    let age = 30;

    writeln!(std::io::stdout(), "Hello, my name is {} and I am {} years old.", name, age).unwrap();
}

printf! 宏

  • 格式化输出到标准输出:使用 printf! 宏输出格式化的字符串。
fn main() {
    let name = "Alice";
    let age = 30;

    printf!("Hello, my name is %s and I am %d years old.\n", name, age);
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!