Rust Learn

2023/06/15 Rust 共 13446 字,约 39 分钟

Rust Learn

Rust 学习记录

Trait

trait对象动态分发

// trait对象动态分发
trait Vehicle {
    fn run(&self);
}
// Car是实现了Vehicle trait的类型
// 只有一个字段表示车牌号
struct Car(u32);
impl Vehicle for Car {
    fn run(&self) {
        println!("car:{}",self.0)
    }
}

// truck是实现了Vehicle trait的类型
// 只有一个字段表示车牌号
struct Truck(u32);
impl Vehicle for Truck {
    fn run(&self) {
        println!("Truck:{}",self.0);
    }
}

fn main(){
    let car = Car(1111);
    let truck = Truck(2222);

    let vehicle1:&dyn Vehicle = &car;
    let vehicle2:&dyn Vehicle = &truck;

    vehicle1.run();
    vehicle2.run();
}

常见的 trait

Display

// std::fmt::Display: 格式化打印用户友好字符串。
use std::fmt;
struct Person{
    name:String,
    age:u32,
}
impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f,"{}({} years)",self.name,self.age)
    }
}
fn main(){
    let _t = Person{
        name:"lll".to_string(),
        age:18,
    };
    println!("P:{}",_t)
}

Debug

// std::fmt::Debug: 格式化打印调试字符串。
#[derive(Debug)]
struct Person{
    name:String,
    age:u32,
}
fn main(){
    let person = Person {
        name: "lll".to_string(),
        age: 18,
    };
    println!("Person: {:?}", person);// Person: Person { name: "lll", age: 18 }
}

PartialEq、Eq

// std::cmp::PartialEq: 比较值相等。
// std::cmp::Eq: 类型完全相等关系。
#[derive(PartialEq,Eq)]
struct Point{
    x:i32,
    y:i32,
}
fn main(){
    let point1 = Point { x: 2, y: 3 };
    let point2 = Point { x: 2, y: 3 };
    let point3 = Point { x: 4, y: 5 };

    println!("point1 == point2: {}", point1 == point2);
    println!("point1 == point3: {}", point1 == point3);
}

PartialOrd、Ord

e.g.1
// std::cmp::PartialOrd: 比较值顺序。
// std::cmp::Ord: 类型完全顺序关系。
#[derive(PartialEq, Eq, PartialOrd, Ord)]  // 部分相等性-https://course.rs/difficulties/eq.html
struct Point {
    x: i32,
    y: i32,
}
fn main(){
    let point1 = Point { x: 2, y: 3 };
    let point2 = Point { x: 4, y: 5 };
    let point3 = Point { x: 2, y: 6 };

    println!("point1 < point2: {}", point1 < point2);
    println!("point1 > point2: {}", point1 > point2);
    println!("point1 <= point3: {}", point1 <= point3);
    println!("point1 >= point3: {}", point1 >= point3);
}
e.g.2
use std::fmt::Display;
struct Pair<T> {
    x: T,
    y: T,
}

impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

impl<T: Display + PartialOrd> Pair<T> {
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}
fn main() {
    let pair = Pair::new(10, 5);
    pair.cmp_display();
}

Clone

// std::clone::Clone: 创建类型副本。
#[derive(Clone)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point1 = Point { x: 2, y: 3 };
    let point2 = point1.clone();

    println!("point1: x = {}, y = {}", point1.x, point1.y);
    println!("point2: x = {}, y = {}", point2.x, point2.y);
}

Add

// std::ops::Add: 定义加法操作。
use std::ops::Add;
struct Point {
    x: i32,
    y: i32,
}
impl Add for Point {
    type Output = Point;

    fn add(self, other: Point) -> Point {
        Point {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}
fn main(){
    let point1 = Point{x:2,y:5};
    let point2 = Point{x:6,y:8};

    let sum = point1+point2;
    println!("sum:x={},y={}",sum.x,sum.y);
}

Mul

// std::ops::Mul: 定义乘法操作。
use std::ops::Mul;
struct Point {
    x: i32,
    y: i32,
}
impl Mul for Point {
    type Output = Point;

    fn mul(self, other: Point) -> Point {
        Point {
            x: self.x * other.x,
            y: self.y * other.y,
        }
    }
}
fn main(){
    let point1 = Point{x:2,y:5};
    let point2 = Point{x:6,y:8};

    let sum = point1*point2;
    println!("sum:x={},y={}",sum.x,sum.y);
}

Iterator

// std::iter::Iterator: 实现迭代器。
struct  Counter{
    count:u32,
}
impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        self.count+=1;
        if self.count<6 {
            Some(self.count)
        }
        else {
            None
        }
    }
}
fn main(){
    let mut counter = Counter{count:0};
    // 使用 for循环迭代Counter
    for num in counter {
        println!("nNum:{}",num);
    }
}

错误处理

栈回溯

// backtrace 栈展开(栈回溯)
// $env:RUST_BACKTRACE=1 ; cargo run
fn main() {
    let v = vec![1, 2, 3];
    v[99];
}

不可恢复的错误

unwrap 和 expect

fn main() {
	use std::net::IpAddr;
	let home: IpAddr = "127.0.0.1".parse().unwrap();
    // let home: IpAddr = "127.0.0.1".parse().expect("fail");
}

可恢复的错误

Result<T, E> 是一个枚举类型,定义如下

enum Result<T, E> { Ok(T), Err(E), }

泛型参数 T 代表成功时存入的正确值的类型,存放方式是 Ok(T),E 代表错误时存入的错误值,存放方式是 Err(E)。

use std::{fs::File, io};
use std::io::Read;

fn readfile1() -> Result<String,io::Error>{
    let file = File::open("path.txt");

    let mut f = match file {
        Ok(file) => file,
        Err(e)=>return Err(e),
    };

    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}
fn readfile2() -> Result<String,io::Error>{
    let mut file1 = File::open("path")?;
    let mut s = String::new();
    file1.read_to_string(&mut s)?;
    Ok(s)
}

fn readfile3() -> Result<String,io::Error>{
    let mut s = String::new();
    File::open("path")?.read_to_string(&mut s)?;
    Ok(s)
}

use std::fs;
fn readfile4() -> Result<String,io::Error>{
    fs::read_to_string("path")
}

fn last_char_of_first_line(text: &str) -> Option<char> {
    text.lines().next()?.chars().last()
}

// fn main() {
//     //let _f = File::open("path.txt").unwrap();
//     //let _f = File::open("path.txt").expect("fail");

//     // let rfile = readfile();
//     // let _result = match rfile {
//     //     Ok(_)=>{println!("ok");}
//     //     Err(_e)=>{println!("err");}
//     // };
//         let text = "Hello\nWorld\n123";
//         let last_char = last_char_of_first_line(text);
    
//         match last_char {
//             Some(c) => println!("Last char of the first line: {}", c),
//             None => println!("Text is empty or the first line is empty."),
//         }
// }

use std::error::Error;
fn main() -> Result<(),Box<dyn Error>>{
    let f = File::open("path")?;
    Ok(())
}

包和模块

包 Crate

创建一个库类型的 Package

cargo new --lib restaurant

模块 Module

src/lib.rs

/* 模块1
mod front_of_house{
    pub mod hosting{
        pub fn add_to_waitlist(){

        }
        fn seat_at_table(){

        }
    }
    mod serving{
        fn take_order(){

        }
        fn serve_order() {
            self::back_of_house::cook_order()
            //back_of_house::cook_order()
        }
        mod back_of_house{
            fn fix_incorrect_order(){
                cook_order();
                super::serve_order();
            }
            pub fn cook_order(){
        
            }
        }
        fn take_payment() {
            
        }
    }
}

pub fn eat_at_restaurant(){
    crate::front_of_house::hosting::add_to_waitlist();
    front_of_house::hosting::add_to_waitlist();
}
*/

/* 模块与文件分离 */

mod front_of_house;

pub use front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}

src/front_of_house.rs

pub mod hosting;
pub mod serving;
src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}

避免同名引用

模块::函数

use std::fmt;
use std::io;

fn function1() -> fmt::Result {
    // --snip--
}

fn function2() -> io::Result<()> {
    // --snip--
}

as别名引用

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    // --snip--
}

fn function2() -> IoResult<()> {
    // --snip--
}

受限的可见性

// 一个名为 `my_mod` 的模块
mod my_mod {
    // 模块中的项默认具有私有的可见性
    fn private_function() {
        println!("called `my_mod::private_function()`");
    }

    // 使用 `pub` 修饰语来改变默认可见性。
    pub fn function() {
        println!("called `my_mod::function()`");
    }

    // 在同一模块中,项可以访问其它项,即使它是私有的。
    pub fn indirect_access() {
        print!("called `my_mod::indirect_access()`, that\n> ");
        private_function();
    }

    // 模块也可以嵌套
    pub mod nested {
        pub fn function() {
            println!("called `my_mod::nested::function()`");
        }

        #[allow(dead_code)]
        fn private_function() {
            println!("called `my_mod::nested::private_function()`");
        }

        // 使用 `pub(in path)` 语法定义的函数只在给定的路径中可见。
        // `path` 必须是父模块(parent module)或祖先模块(ancestor module)
        pub(in crate::my_mod) fn public_function_in_my_mod() {
            print!("called `my_mod::nested::public_function_in_my_mod()`, that\n > ");
            public_function_in_nested()
        }

        // 使用 `pub(self)` 语法定义的函数则只在当前模块中可见。
        pub(self) fn public_function_in_nested() {
            println!("called `my_mod::nested::public_function_in_nested");
        }

        // 使用 `pub(super)` 语法定义的函数只在父模块中可见。
        pub(super) fn public_function_in_super_mod() {
            println!("called my_mod::nested::public_function_in_super_mod");
        }
    }

    pub fn call_public_function_in_my_mod() {
        print!("called `my_mod::call_public_funcion_in_my_mod()`, that\n> ");
        nested::public_function_in_my_mod();
        print!("> ");
        nested::public_function_in_super_mod();
    }

    // `pub(crate)` 使得函数只在当前包中可见
    pub(crate) fn public_function_in_crate() {
        println!("called `my_mod::public_function_in_crate()");
    }

    // 嵌套模块的可见性遵循相同的规则
    mod private_nested {
        #[allow(dead_code)]
        pub fn function() {
            println!("called `my_mod::private_nested::function()`");
        }
    }
}

fn function() {
    println!("called `function()`");
}

fn main() {
    // 模块机制消除了相同名字的项之间的歧义。
    function();
    my_mod::function();

    // 公有项,包括嵌套模块内的,都可以在父模块外部访问。
    my_mod::indirect_access();
    my_mod::nested::function();
    my_mod::call_public_function_in_my_mod();

    // pub(crate) 项可以在同一个 crate 中的任何地方访问
    my_mod::public_function_in_crate();

    // pub(in path) 项只能在指定的模块中访问
    // 报错!函数 `public_function_in_my_mod` 是私有的
    //my_mod::nested::public_function_in_my_mod();
    // 试一试 ^ 取消该行的注释

    // 模块的私有项不能直接访问,即便它是嵌套在公有模块内部的

    // 报错!`private_function` 是私有的
    //my_mod::private_function();
    // 试一试 ^ 取消此行注释

    // 报错!`private_function` 是私有的
    //my_mod::nested::private_function();
    // 试一试 ^ 取消此行的注释

    // 报错! `private_nested` 是私有的
    //my_mod::private_nested::function();
    // 试一试 ^ 取消此行的注释
}

注释和文档

包和模块级别的注释

/*! lib包是world_hello二进制包的依赖包,
 里面包含了compute等有用模块 */

pub mod front_of_house;
/* 
        再为该包根的子模块 src/front_of_house.rs 添加注释:
        //! 计算一些你口算算不出来的复杂算术题
        /// `add_one`将指定值加1
        ///
*/

代码注释

// 块注释

/* */ 行注释

文档注释

文档块注释

// 文档块注释/* ..... */
/** `add` 将指定值加2
```
let arg = 5;
let answer = my_crate::add(arg);

assert_eq!(7, answer);
```
*/
pub fn add(x: i32) -> i32 {
    x + 2
}

文档行注释

// 带测试用例的文档测试 文档行注释 ///
/// `add_one` 将指定值加1
///
/// # Examples11
///
/// ```
/// let arg = 5;
/// let answer = world_hello::compute::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

文档测试

造成 panic 的文档测试

// 造成 panic 的文档测试    should_panic
/// # Panics
///
/// The function panics if the second argument is zero.
///
/// ```rust,should_panic
/// // panics on division by zero
/// world_hello::compute::div(10, 0);
/// ```
pub fn div(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Divide-by-zero error");
    }

    a / b
}

保留测试,隐藏文档

// 保留测试,隐藏文档
/// ```
/// # // 使用#开头的行会在文档中被隐藏起来,但是依然会在文档测试中运行
/// # fn try_main() -> Result<(), String> {
/// let res = world_hello::compute::try_div(10, 0)?;
/// # Ok(()) // returning from try_main
/// # }
/// # fn main() {
/// #    try_main().unwrap();
/// #
/// # }
/// ```
pub fn try_div(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Divide-by-zero"))
    } else {
        Ok(a / b)
    }
}

文档注释中的代码跳转

跳转到标准库

// 跳转到标准库
/// `add_one` 返回一个[`Option`]类型
pub fn add_one(x: i32) -> Option<i32> {
    Some(x + 1)
}

使用路径的方式跳转

// 使用路径的方式跳转
use std::sync::mpsc::Receiver;

/// [`Receiver<T>`]   [`std::future`].
///
///  [`std::future::Future`] [`Self::recv()`].
pub struct AsyncReceiver<T> {
    sender: Receiver<T>,
}

impl<T> AsyncReceiver<T> {
    pub async fn recv() -> T {
        unimplemented!()
    }
}

使用完整路径跳转到指定项

// 使用完整路径跳转到指定项
pub mod a {
    /// `add_one` 返回一个[`Option`]类型
    /// 跳转到[`crate::MySpecialFormatter`]
    pub fn add_one(x: i32) -> Option<i32> {
        Some(x + 1)
    }
}

pub struct MySpecialFormatter;

同名项的跳转

// 同名项的跳转

/// 跳转到结构体  [`Foo`](struct@Foo)
pub struct Bar;

/// [`Foo`](struct@Foo)
/// 跳转到同名函数 [`Foo`](fn@Foo)
pub struct Foo {}

/// 跳转到同名宏 [`foo!`]
pub fn Foo() {}

#[macro_export]
macro_rules! foo {
  () => {}
}

文档搜索别名

// 文档搜索别名
#[doc(alias = "x")]
#[doc(alias = "big")]
pub struct BigX;

#[doc(alias("y", "big"))]
pub struct BigY;

格式化输出

  • print! 将格式化文本输出到标准输出,不带换行符
  • println! 同上,但是在行的末尾添加换行符
  • format! 将格式化文本输出到 String 字符串
  • eprint!,eprintln! 仅应该被用于输出错误信息和进度信息

Display

为自定义类型实现 Display 特征

// 为自定义类型实现 Display 特征
struct PersonInfo {
    name: String,
    age: u8,
}

use std::fmt;
impl fmt::Display for PersonInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "姓名{},年龄{}",
            self.name, self.age
        )
    }
}
fn main() {
    let p = PersonInfo {
        name: "sunface".to_string(),
        age: 18,
    };
    println!("{}", p);
}

为外部类型实现 Display 特征

// 为外部类型实现 Display 特征
 struct Array(Vec<i32>);

 use std::fmt;
 impl fmt::Display for Array {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "数组是:{:?}", self.0)
     }
 }
 fn main() {
     let arr = Array(vec![1, 2, 3]);
     println!("{}", arr);
 }

位置参数

占位符(索引从 0 开始):

fn main() {
    println!("{}{}", 1, 2); // =>"12"
    println!("{1}{0}", 1, 2); // =>"21"
    // => Alice, this is Bob. Bob, this is Alice
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
    println!("{1}{}{0}{}", 1, 2); // => 2112
}

具名参数

为参数指定名称:

fn main() {
    println!("{argument}", argument = "test"); // => "test"
    println!("{name} {}", 1, name = 2); // => "2 1"
    println!("{a} {c} {b}", a = "a", b = 'b', c = 3); // => "a 3 b"
}

带名称的参数必须放在不带名称参数的后面

格式化参数

fn main() {
    let v = 3.1415926;
    // Display => 3.14
    println!("{:.2}", v);
    // Debug => 3.14
    println!("{:.2?}", v);
}

填充和对齐

字符串填充

字符串格式化默认使用空格进行填充,并且进行左对齐。

fn main() {
    //-----------------------------------
    // 以下全部输出 "Hello x    !"
    // 为"x"后面填充空格,补齐宽度5
    println!("Hello {:5}!", "x");
    // 使用参数5来指定宽度
    println!("Hello {:1$}!", "x", 5);
    // 使用x作为占位符输出内容,同时使用5作为宽度
    println!("Hello {1:0$}!", 5, "x");
    // 使用有名称的参数作为宽度
    println!("Hello {:width$}!", "x", width = 5);
    //-----------------------------------

    // 使用参数5为参数x指定宽度,同时在结尾输出参数5 => Hello x    !5
    println!("Hello {:1$}!{}", "x", 5);
}

数字填充

数字格式化默认也是使用空格进行填充,但与字符串左对齐不同的是,数字是右对齐。

fn main() {
    // 宽度是5 => Hello     5!
    println!("Hello {:5}!", 5);
    // 显式的输出正号 => Hello +5!
    println!("Hello {:+}!", 5);
    // 宽度5,使用0进行填充 => Hello 00005!
    println!("Hello {:05}!", 5);
    // 负号也要占用一位宽度 => Hello -0005!
    println!("Hello {:05}!", -5);
}

对齐

fn main() {
    // 以下全部都会补齐5个字符的长度
    // 左对齐 => Hello x    !
    println!("Hello {:<5}!", "x");
    // 右对齐 => Hello     x!
    println!("Hello {:>5}!", "x");
    // 居中对齐 => Hello   x  !
    println!("Hello {:^5}!", "x");

    // 对齐并使用指定符号填充 => Hello x&&&&!
    // 指定符号填充的前提条件是必须有对齐字符
    println!("Hello {:&<5}!", "x");
}

精度

精度可以用于控制浮点数的精度或者字符串的长度

fn main() {
    let v = 3.1415926;
    // 保留小数点后两位 => 3.14
    println!("{:.2}", v);
    // 带符号保留小数点后两位 => +3.14
    println!("{:+.2}", v);
    // 不带小数 => 3
    println!("{:.0}", v);
    // 通过参数来设定精度 => 3.1416,相当于{:.4}
    println!("{:.1$}", v, 4);

    let s = "hi我是Sunface孙飞";
    // 保留字符串前三个字符 => hi我
    println!("{:.3}", s);
    // {:.*}接收两个参数,第一个是精度,第二个是被格式化的值 => Hello abc!
    println!("Hello {:.*}!", 3, "abcdefg");
}

进制

可以使用 # 号来控制数字的进制输出:

  • #b, 二进制
  • #o, 八进制
  • #x, 小写十六进制
  • #X, 大写十六进制
  • x, 不带前缀的小写十六进制
fn main() {
    // 二进制 => 0b11011!
    println!("{:#b}!", 27);
    // 八进制 => 0o33!
    println!("{:#o}!", 27);
    // 十进制 => 27!
    println!("{}!", 27);
    // 小写十六进制 => 0x1b!
    println!("{:#x}!", 27);
    // 大写十六进制 => 0x1B!
    println!("{:#X}!", 27);

    // 不带前缀的十六进制 => 1b!
    println!("{:x}!", 27);

    // 使用0填充二进制,宽度为10 => 0b00011011!
    println!("{:#010b}!", 27);
}

指数

fn main() {
    println!("{:2e}", 1000000000); // => 1e9
    println!("{:2E}", 1000000000); // => 1E9
}

指针地址

let v= vec![1, 2, 3];
println!("{:p}", v.as_ptr()) // => 0x600002324050

转义

fn main() {
    // " 转义为 '{'   " 转义为 '}'   "\"" 转义为 '"'
    // => Hello "{World}" 
    println!(" Hello \"\" ");

    // 下面代码会报错,因为占位符{}只有一个右括号},左括号被转义成字符串的内容
    // 也不可使用 '\' 来转义 "{}"
    // println!(" \{ Hello \} ")
}

在格式化字符串时捕获环境中的值

输出一个函数的返回值

/* 以前的写法
	fn get_person() -> String {
	    String::from("sunface")
	}
	fn main() {
	    let p = get_person();
	    println!("Hello, {}!", p);                // implicit position
	    println!("Hello, {0}!", p);               // explicit index
	    println!("Hello, {person}!", person = p);
	}
*/

fn get_person() -> String {
    String::from("sunface")
}
fn main() {
    let person = get_person();
    println!("Hello, {person}!");
}

文档信息

Search

    Table of Contents