rus语法

打印

#[derive(Debug)] // 有了这才能通过 {:?} {:#?} debug
struct Complex {
    real: f64,
    imag: f64,
}
impl fmt::Display for Complex {
    // 默认泛型容器没有实现,要手动实现才能使用 {} 打印
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "x: {}, y: {}", self.real, self.imag)
    }
}

/// this is doc comment
/// ...
fn print() {
    /* format!();print!();println!();eprint!();eprintln!(); */
    let variable = 1;
    println!("{} 为占位符", variable);
    println!(
        "named argument: {subject} {verb} {object}",
        subject = "A",
        verb = "hava",
        object = "ball"
    );

    println!("Base 10:               {}", 69420); //69420
    println!("Base 2 (binary):       {:b}", 69420); //10000111100101100
    println!("Base 8 (octal):        {:o}", 69420); //207454
    println!("Base 16 (hexadecimal): {:x}", 69420); //10f2c
    println!("Base 16 (hexadecimal): {:X}", 69420); //10F2C

    println!(
        "打印详细信息:{o} {o:?} {o:#?}",
        o = Complex {
            real: 1.1,
            imag: 3.2
        }
    ); // 默认不可打印,实现 Debug 接口才可
}
// 可打印列表
struct List(Vec<i32>);

impl fmt::Display for List {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let vec = &self.0;

        write!(f, "[")?; // ? 用于处理 Result 返回值,出错则直接结束函数返回error,否则继续

        for (i, ele) in vec.iter().enumerate() {
            if i != 0 {
                write!(f, ",")?;
            }
            write!(f, "{} : {}", i, ele)?;
        }

        write!(f, "]")
    }
}

更多格式见:https://doc.rust-lang.org/std/fmt/

基本

操作符类似C

fn main() {
    // Integer addition
    println!("1 + 2 = {}", 1u32 + 2);

    // Integer subtraction
    println!("1 - 2 = {}", 1i32 - 2);
    // TODO ^ Try changing `1i32` to `1u32` to see why the type is important

    // Short-circuiting boolean logic
    println!("true AND false is {}", true && false);
    println!("true OR false is {}", true || false);
    println!("NOT true is {}", !true);

    // Bitwise operations
    println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
    println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
    println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
    println!("1 << 5 is {}", 1u32 << 5);
    println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);

    // Use underscores to improve readability!
    println!("One million is written as {}", 1_000_000u32);
}

tuple

fn fun() {
    let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
    println!("tuple of tuples: {:?}", tuple_of_tuples);

    let matrix = Matrix(1.1f32, 1.2f32, 1.3, 1.4);
    println!("Matrix:\n{}", matrix);
    println!("Transpose:\n{}", transpose(matrix));
}

struct Matrix(f32, f32, f32, f32);
impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "( {} {} )\n( {} {})", self.0, self.1, self.2, self.3)
    }
}

fn transpose(matrix: Matrix) -> Matrix {
    let Matrix(e0, e1, e2, e3) = matrix;  // Destructuring
    Matrix(e0, e1, e3, e2)
}

切片:占用两个字,一个为指向数据的指针,另一个为长度。

fn analyze_slice(slice: &[i32]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}
fn fun() {
    let arr1: [i32; 5] = [1,2,3,4,5];
    let arr2: [i32; 10] = [0; 10];
    println!("{} {} {}", arr1[0], arr2.len(), std::mem::size_of_val(&arr1));

    // 数组被自动借用为切片,空切片:&[]
    analyze_slice(&arr1);
    analyze_slice(&arr2[3 .. 6]);

    // 使用 get 方法得到被 Option 包裹的数据,可以使用 match 或 expect 方法处理
    for i in 0..arr2.len() + 1 {
        match arr2.get(i) {
            Some(val) => println!("{}: {}", i, val),
            None => println!("over the bound!"),
        }
    }
}

结构体 枚举类型 别名 静态变量

结构体

// 无字段可用于泛型
struct Unit;

// struct tuple 即命名数组
struct Pair(i32, i32);

#[derive(Debug)]
struct Point {
    x: f32,
    y: f32,
}
struct Rectangle {
    top_left: Point,
    bottom_right: Point,
}
impl Rectangle {
    fn area(&self) -> f32 {
        (self.bottom_right.x - self.top_left.x) * (self.bottom_right.y - self.top_left.y)
    }
}

fn square(p: Point, l: f32) -> Rectangle {
    Rectangle {
        bottom_right: Point { // 注意这里要在 top_left 之前声明,否则p被 borrowed ,就无法再使用
            x: p.x + l,
            y: p.y + l,
        },
        top_left: p,
    }
}

fn fun() {
    let point1 = Point{x:1.1, y:1.2};
    
    // 使用结构体更新语法引用其他结构体数据
    let point2 = Point{x:2.1, ..point1};

    // destructure
    let Point { x: left_edge, y: top_edge } = point2;

    let _rec = Rectangle {
        top_left: Point { x: left_edge, y: top_edge },
        bottom_right: point1,  // point1 be borrowed
    };
}

 枚举类型,use 与命名空间

enum Event {
    PageLoad,       // unit struct
    KeyPress(char), // struct tuple
    Paste(String),
    Click { x: i64, y: i64 }, // struct
}
fn inspect(event: Event) {
    // 使用 use 后无需使用命名空间
    use crate::Event::{KeyPress, PageLoad}; // use create::Event::*;
    match event {
        PageLoad => println!("page loaded"),
        KeyPress(c) => println!("pressed '{}'.", c),
        Event::Paste(s) => println!("pasted \"{}\".", s),
        Event::Click { x, y } => {
            println!("clicked at x={}, y={}.", x, y);
        }
    }
}
#![allow(dead_code)]

use std::fmt::Display;
use crate::List::*;

enum List<T: Display> {
    Cons(T, Box<List<T>>),
    Nil,
}
impl<T: Display> List<T> {
    fn new() -> List<T> {
        List::Nil
    }
    // self 代表对象,所有权会移交
    fn prepend(self, elem: T) -> List<T> {
        List::Cons(elem, Box::new(self))
    }
    // &self 代表引用,所有权不会移交
    fn len(&self) -> u64 {
        match self {
            List::Cons(_, tail) => 1 + tail.len(),
            Nil => 0,
        }
    }
    fn stringify(&self) -> String {
        match self {
            List::Cons(head, tail) => {
                format!("{}, {}", head, tail.stringify())
            }
            List::Nil => {
                format!("Nil")
            }
        }
    }
}

fn main() {
    let mut list = List::<i32>::new();

    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    println!("{}", list.stringify()); // 3, 2, 1, Nil
}

enum 类C用法

// enum with implicit discriminator (starts at 0)
enum Number {
    Zero,
    One,
    Two,
}

// enum with explicit discriminator
enum Color {
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {
    // `enums` can be cast as integers.
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}

 别名

type AliasType = SomeType;

 静态变量

  • const:不可变,常用
  • static:可能是 mut 类型,被推断为静态生命周期 'static,访问修改都是 unsafe 的

类型转换

使用 use 显现类型转换,rust 没有隐式类型转换

let decimal = 65.4321_f32;
let integer = decimal as u8;
let char = integer as char;

// 300.0 as u8 is 255
println!(" 300.0 as u8 is : {}", 300.0_f32 as u8);
// -100.0 as u8 is 0
println!("-100.0 as u8 is : {}", -100.0_f32 as u8);
// nan as u8 is 0
println!("   nan as u8 is : {}", f32::NAN as u8);

 内置类型转换 From Into

let my_str = "hello";
let my_string = String::from(my_str);

struct Number {
    value: i32,
}
impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}

let num = Number::from(30);

let int = 5;
let num: Number = int.into();

try_from / try_into 返回一个 Result

#[derive(Debug, PartialEq)]
struct EvenNumber(i32);

impl TryFrom<i32> for EvenNumber {
    type Error = ();

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value % 2 == 0 {
            Ok(EvenNumber(value))
        } else {
            Err(())
        }
    }
}

使用 Display 转为 String

使用字符串实现的 FromStr 接口的 parse 方法将 String 转为其他类型

let parsed: i32 = "5".parse().unwrap();
let turbo_parsed = "10".parse::<i32>().unwrap();

控制流

let x = if n < 100.0 {
    1
}  else if n < 10.0 {
    2
} else {
    3
};

// loop 死循环 可搭配 break continue
fn main() {
    'outer: loop {
        println!("Entered the outer loop");
        'inner: loop {
            println!("Entered the inner loop");
            // This breaks the outer loop
            break 'outer;
        }
        println!("This point will never be reached");
    }
    println!("Exited the outer loop");
}
/*
Entered the outer loop
Entered the inner loop
Exited the outer loop
*/

let result = loop {
    counter += 1;

    if counter == 10 {
        break counter * 2;
    }
};

while 搭配布尔语句

for n in 1..101 {

}
for n in 1..=100 {

}

let names = vec!["Bob", "Frank", "Ferris"];

// 元素被借用,循环后可再用
for name in names.iter() {
    match name {
        &"Ferris" => println!("There is a rustacean among us!"),
        _ => println!("Hello {}", name),
    }
}

// 元素可以被原地修改,循环后可用
for name in names.iter_mut() {
    *name = match name {
        &mut "Ferris" => "There is a rustacean among us!",
        _ => "Hello",
    }
}

// 元素被移动,循环后不可再用
for name in names.into_iter() {
    match name {
        "Ferris" => println!("There is a rustacean among us!"),
        _ => println!("Hello {}", name),
    }
}

 match destructuring

match number {
    1 => println!("One!"),
    2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
    13..=19 => println!("A teen"),
    _ => println!("Ain't special"),  // 处理其他情况,不可缺少
}

let arr = 数组或切片;
match arr {
    [0, second, third] => print!(),
    [1, _, third] => print!(),
    // 绑定一些忽略一些
    [2, second, ..] => print!(),
    // 将剩余的绑定到数组或切片
    [3, second, tail @ ..] => print!(),
    [first, middle @ .., last] => print!(),
}
match tuple {
    (0, x, y) if x > 0 => println!(""), // 分支守卫
    (3, .., 4) => println!(""),
    _ => print!(),
}
let reference = &4;
match reference {
    &val => print!("这里变量被解引用"),
}

match value {
    ref r => print!("创建新引用"),
}

match value {
    ref mut m => print!("可变引用"),
}

match value {
    Foo { x: 1, y: a} => print!(""),
    Foo { x: a, y: 1} => print!(""),
}
// 使用 @ 绑定对象
match age() {
    0 => xx,
    n @ 1 ..= 12 => xx,
    n @ 13 ..= 19 => xx,
    n => xx,
}

闭包

fn main() {
    /****************** borrow / reference *******************/
    let mut outer_var = 42;
    let closure_annotated = |i: i32| -> i32 { i + outer_var }; // immutrably borrow
    let mut closure_inferred = |i| outer_var += i; // mutably borrow

    closure_inferred(1); // 有了这句使用后才能推断出闭包类型
    // println!("{}", outer_var); // 在其他借用没有结束时借用,会报错
    closure_inferred(1);
    println!("{}", outer_var); // 此处不会报错

    /****************** move *******************/
    let movable = Box::new(3);
    let consume = || {
        println!("`movable`: {:?}", movable);
        std::mem::drop(movable); // drop函数需要传入move值,此处发生 move
    };

    consume();
    // consume(); 因为变量只能move一次,所以再次调用会报错
    
    let contains = move |needle| haystack.contains(needle);  // 在 | 前使用 move 关键字,强制move
}

当闭包作为函数参数时,需要用接口指明 参数为闭包 还有 闭包的参数捕获方式:Fn FnMut FnOnce。权限逐渐变严格,FnOnce接收更宽松的两类接口,FnMut也可传入Fn接口

fn apply<F>(f: F)
where
    F: FnOnce(),
{
    f();
}
fn main() {
    use std::mem;

    let mut w = "hello".to_owned(); // create owned data
    let f = || {
        mem::drop(w);
    };
    apply(f); // 函数f的参数是 move 来的,apply中必须要用 FnOnce
}

当闭包作为输出时要用 impl 和接口指明类型,同时还要用 move 将捕获变量转移出,否者在函数结束后引用变量销毁。

fn create_fn() -> impl Fn() {
    let text = "Fn".to_owned();

    move || println!("This is a: {}", text)
}

fn create_fnmut() -> impl FnMut() {
    let text = "FnMut".to_owned();

    move || println!("This is a: {}", text)
}

fn create_fnonce() -> impl FnOnce() {
    let text = "FnOnce".to_owned();

    move || println!("This is a: {}", text)
}

fn main() {
    let fn_plain = create_fn();
    let mut fn_mut = create_fnmut();
    let fn_once = create_fnonce();

    fn_plain();
    fn_mut();
    fn_once();
}

函数式编程

// Functional approach 所有奇数的平方
let sum_of_squared_odd_numbers: u32 =
    (0..).map(|n| n * n)                             // All natural numbers squared
    .take_while(|&n_squared| n_squared < upper) // Below upper limit
    .filter(|&n_squared| is_odd(n_squared))     // That are odd
    .sum();                                     // Sum them
println!("functional style: {}", sum_of_squared_odd_numbers);

Diverging functions

不会正常返回的函数

fn foo() -> ! {
    panic!("This call never returns.");
}

#![feature(never_type)]

fn main() {
    let x: ! = panic!("This call never returns.");
    println!("You will never see this line!");
}

fn main() {
    fn sum_odd_numbers(up_to: u32) -> u32 {
        let mut acc = 0;
        for i in 0..up_to {
            // Notice that the return type of this match expression must be u32
            // because of the type of the "addition" variable.
            let addition: u32 = match i%2 == 1 {
                // The "i" variable is of type u32, which is perfectly fine.
                true => i,
                // On the other hand, the "continue" expression does not return
                // u32, but it is still fine, because it never returns and therefore
                // does not violate the type requirements of the match expression.
                false => continue,
            };
            acc += addition;
        }
        acc
    }
    println!("Sum of odd numbers up to 9 (excluding): {}", sum_odd_numbers(9));
}

模块

模块默认都是私有的,使用 pub 关键字修饰后其他模块才可见。
结构体的字段与方法也都默认私有。

mod my_mod {
    fn private_fun(){}
    pub fn public_fun(){}

    pub mod nested {
        pub(in crate::my_mod) fn public_in_given_mod(){}
        pub(super) fn public_in_super_super_mod(){}
        pub(crate) fn same_as_private(){}
    }
}

use 声明

use crate::deeply::nested::{
    my_first_function,
    my_second_function,
    AndATraitType
};

use deeply::nested::function as other_function;

super & self

mod mod1 {
    pub(super) fn fun1() {
        println!("fun1");
    }
}

mod mod2 {
    mod mod3 {
        pub fn fun2() {
            println!("fun2");
        }
    }
    fn fun() {
        self::mod3::fun2();
        super::mod1::fun1();
    }
}
$ tree .
.
├── my
│   ├── inaccessible.rs    my::nested::xxx
│   └── nested.rs          my::nested::xxx
├── my.rs                     my::xxx
└── split.rs

在 split.rs 文件头部输入 mod my; 使 my.rs 内部自动创建模块my并插入 my.rs 的内容
在 my.rs 文件头部插入 mod inaccessible; 和 pub mod nested;

|--main.rs                       通过 pub mod util; 引入util模块
|--util.rs
|--util/
       |--mod.rs
       |--math.rs
注意:utils.rs 和 util/mod.rs 内容都是util模块下的内容;二者同时存在会冲突

Cargo & Crates

crates.io: Rust Package Registry https://crates.io

cargo new foo       # A binary
cargo new --lib bar # A library
cargo build
cargo run

.
├── bar
│   ├── Cargo.toml
│   └── src
│       └── lib.rs   # 库
│       └── main.rs  # 可执行文件
└── foo
    ├── Cargo.toml
    └── src
        └── lib.rs   # 库
        └── main.rs  # 可执行文件

一个 crate 包中生成多个可执行文件

foo
├── Cargo.toml
└── src
    ├── main.rs
    └── bin
        └── my_other_bin.rs
# cargo build                      生成默认文件
# cargo build --bin my_other_bin   指定可执行文件,文件名为 my_other_bin

Attributes

修饰crate、条件编译、格式化、链接、单元测试、压力测试、类宏属性

#![allow(dead_code)] // #! 开头,修饰crate


// 三种修饰 mod fn 的格式
#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value)]

cfg 代指 configure,在使用 rustc 编译时可以通过 --cfg 指定

  • #[cfg(...)] 条件编译
  • cfg!(...) 在运行时返回布尔值
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
    println!("You are running linux!");
}
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
    println!("You are *not* running linux!");
}

fn main() {
    are_you_on_linux();
    println!("Are you sure?");
    if cfg!(target_os = "linux") {
        println!("Yes. It's definitely linux!");
    } else {
        println!("Yes. It's definitely *not* linux!");
    }
}

 

泛型

接口约束也可以是空接口

struct Empty;
struct Null;
/* impl Display for Empty, Null */

trait Drop<T> {
    fn drop(self, _: T);
}
impl<T: Display, U> Drop for U  // 此处 T 被加上了 Display 接口的约束
where                           // where 用于让接口约束更美观
    U: Display + Debug,         // 组合多个不同接口
{
    fn drop(self, _: T) {}      // 此处 self 和参数都 move 了过来,函数返回时变量死亡
}

fn main() {
    let empty = Empty;
    let null = Null;
    empty.drop(null);           // empty 和 null 都move到方法内,之后死亡
}

不得不用 where 进行约束的场景:

impl<T> PrintInOption for T 
where
    Option<T>: Debug          // 因为用于打印的是 Option<T>,所以无法通过正常语法约束T
{
    fn print_in_option(self) {
        println!("{:?}", Some(self));
    }
}

使用类型关联使得容器中某种些变量的类型由其他变量决定

struct Container(i32, i32);

trait Contains {
    // Define generic types here which methods will be able to utilize.
    type A;
    type B;

    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

impl Contains for Container {
    type A = i32;
    type B = i32;

    fn contains(&self, number_1: &Self::A, number_2: &Self::B) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    fn first(&self) -> i32 { self.0 }
    fn last(&self) -> i32 { self.1 }
}

// 不使用类型绑定的写法:fn difference<A, B, C>(container: &C) -> i32 where C: Contains<A, B> { ... }
fn difference<C: Contains>(container: &C) -> i32 {
    container.last() - container.first()
}

作用域规则

struct ToDrop;

// 析构函数,非必须,销毁时自动调用
impl Drop for ToDrop {
    fn drop(&mut self) {
        println!("ToDrop is being dropped");
    }
}

fn main() {
    let x = ToDrop;
    println!("Made a ToDrop!");
}

所有权

所有资源只有一个拥有者,但不是所有变量都有资源。例如 let x = y foo(x) 资源所有权发生转移,即 move,解决了悬挂指针的问题。

在 move 时 Mutability 可以改变

fn destory_box(c: Box<i32>) {
    println!("Destroying a box contains {}", c);
}

fn main() {
    let x = 5u32;             // stack alloc
    let y = x;                // u32 have Copy trait
    println!("{} {}", x, y);  // 引用

    let a = Box::new(5i32);
    let b = a;                // move
    destory_box(b);
}
let Person { 
    name,    // move
    ref age  // borrow
} = Person {
    name: String::from("Alice"),
    age: Box::new(20),
};

借用

函数传参时通过 &T 得到参数的引用,&mut T为可变引用。

  • 在不可变引用使用期间,元对象不可被可变引用
  • 同一时间段只能有一个可变引用
  • 在最后一个可变引用出现的语句之后,此可变引用生命结束
fn main() {
    let mut i = 1;
    let i1 = &i;
    let ref i11 = i;            // 使用 ref 声明引用
    let i2 = &mut i;
    // let i3 = &mut i;         // 此处会报错,因为多个可变引用冲突
    // i = 2;                   // 此处报错,因为i被借用
    println!("{}", i2);         // 可变引用i2最后一次出现
    let i3 = &mut i;
    println!("{}", i3);
    // println!("{}", i1);      // 如果有这个语句,前面两个可变引用也出错
}
// 在 destructuring 中使用ref指定部分字段为引用
let Point { 
    x: ref ref_to_x, 
    y: _ 
} = point;

生命周期

// Lifetimes are annotated below with lines denoting the creation
// and destruction of each variable.
// `i` has the longest lifetime because its scope entirely encloses 
// both `borrow1` and `borrow2`. The duration of `borrow1` compared 
// to `borrow2` is irrelevant since they are disjoint.
fn main() {
    let i = 3; // Lifetime for `i` starts. ────────────────┐
    //                                                     │
    { //                                                   │
        let borrow1 = &i; // `borrow1` lifetime starts. ──┐│
        //                                                ││
        println!("borrow1: {}", borrow1); //              ││
    } // `borrow1` ends. ─────────────────────────────────┘│
    //                                                     │
    //                                                     │
    { //                                                   │
        let borrow2 = &i; // `borrow2` lifetime starts. ──┐│
        //                                                ││
        println!("borrow2: {}", borrow2); //              ││
    } // `borrow2` ends. ─────────────────────────────────┘│
    //                                                     │
}   // Lifetime ends. ─────────────────────────────────────┘

有生命周期显式注释的函数签名

在函数中使用泛型和符号 'a 'b 'c ... 限制引用的生命周期

// `print_refs` takes two references to `i32` which have different
// lifetimes `'a` and `'b`. These two lifetimes must both be at
// least as long as the function `print_refs`.
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
    println!("x is {} and y is {}", x, y);
}

// A function which takes no arguments, but has a lifetime parameter `'a`.
fn failed_borrow<'a>() {
    let _x = 12;

    // ERROR: `_x` does not live long enough
    let y: &'a i32 = &_x;
    // Attempting to use the lifetime `'a` as an explicit type annotation 
    // inside the function will fail because the lifetime of `&_x` is shorter
    // than that of `y`. A short lifetime cannot be coerced into a longer one.
}

fn main() {
    // Create variables to be borrowed below.
    let (four, nine) = (4, 9);
    
    // Borrows (`&`) of both variables are passed into the function.
    print_refs(&four, &nine);
    // Any input which is borrowed must outlive the borrower. 
    // In other words, the lifetime of `four` and `nine` must 
    // be longer than that of `print_refs`.
    
    failed_borrow();
    // `failed_borrow` contains no references to force `'a` to be 
    // longer than the lifetime of the function, but `'a` is longer.
    // Because the lifetime is never constrained, it defaults to `'static`.
}

 

// One input reference with lifetime `'a` which must live  输入引用的生命至少与函数一样长
// at least as long as the function.
// Mutable references are possible with lifetimes as well.
fn add_one<'a>(x: &'a mut i32) {
    *x += 1;
}

// Multiple elements with different lifetimes. In this case, it
// would be fine for both to have the same lifetime `'a`, but
// in more complex cases, different lifetimes may be required.
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
    println!("`print_multi`: x is {}, y is {}", x, y);
}

// Returning references that have been passed in is acceptable.
// However, the correct lifetime must be returned.
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x }

// 注意返回值的生命周期必须 与输入生命周期相同 或 为static
fn invalid_output<'a>() -> &'a String { &String::from("foo") }
// The above is invalid: `'a` must live longer than the function.
// Here, `&String::from("foo")` would create a `String`, followed by a
// reference. Then the data is dropped upon exiting the scope, leaving
// a reference to invalid data to be returned.

 结构体和方法都可以有生命周期标注

struct NamedBorrowed<'a> {
    x: &'a i32,
    y: &'a i32,
}
impl<'a> Default for NamedBorrowed<'a> {
    fn default() -> Self {
        Self {
            x: &10,
        }
    }
}

// method 与 function 一样,都可使用生命周期
struct Owner(i32);
impl Owner {
    // Annotate lifetimes as in a standalone function.
    fn add_one<'a>(&'a mut self) { self.0 += 1; }
    fn print<'a>(&'a self) {
        println!("`print`: {}", self.0);
    }
}

泛型的生命周期约束

泛型要满足指定接口且生命周期要长于指定符号

fn print_ref<'a, T>(t: &'a T) 
where
    T: Debug + 'a 
{
    println!("`print_ref`: t is {:?}", t);
}

强制转换

生命周期存在差异时强制转换为较短的情况

// 输入变量生命周期不同,a 成为较短的那个
fn multiply<'a>(first: &'a i32, second: &'a i32) -> i32 {
    first * second
}

// a 至少和 b 一样长,返回的生命周期被转换为 b
fn choose_first<'a: 'b, 'b>(first: &'a i32, _: &'b i32) -> &'b i32 {
    first
}

fn main() {
    let first = 2; // Longer lifetime
    
    {
        let second = 3; // Shorter lifetime
        
        println!("The product is {}", multiply(&first, &second));
        println!("{} is the first", choose_first(&first, &second));
    };
}

Static 静态生命周期 

'static 对应变量存储在二进制的只读空间,有2种创建方法:

  1. 使用 static 关键字声明
  2. 创建 &'static str 类型的字符串 
static NUM:i32 = 1;
let static_string = "I'm in read-only memory";

在签名中可以使用 'static 约束

fn print_it( input: impl Debug + 'static ) {
    println!( "'static value passed in is: {:?}", input );
}

fn main() {
    // i is owned and contains no references, thus it's 'static:
    let i = 5;
    print_it(i);

    // Oops!!!  &i only has the lifetime defined by the scope of main(), so it's not 'static:
    print_it(&i);
}

生命周期是一定存在的,只是大多数情况下可以省略并由编译器推断

接口

某些简单的内置接口可通过 #[derive] 实现,也可手动实现更复杂版本

  • Comparison traits: Eq, PartialEq, Ord, PartialOrd.
  • Clone, to create T from &T via a copy.
  • Copy, to give a type 'copy semantics' instead of 'move semantics'.
  • Hash, to compute a hash from &T.
  • Default, to create an empty instance of a data type.
  • Debug, to format a value using the {:?} formatter.

使用 dyn 返回接口

在 rust 中返回值大小需要固定大小,无法直接返回一个 trait,可以返回一个指向接口的 Box 指针。但 rust 要求尽可能的显示声明堆内存分配,所以要加上 dyn

fn fun() -> Box<dyn Display> {
    Box::new(A_Struct_impl_Display{})
}

操作符重载

实现 std::ops 下的接口,如 std::ops::Add std::ops::Sub core::ops - Rust (rust-lang.org) doc.rust-lang.org/core/ops/

use std::ops::{Add, Mul};

#[derive(Debug)]
struct Foo;
impl Add for Foo {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        rhs
    }
}
#[derive(Debug)]
struct Bar;
impl Mul<Foo> for Bar {         // 注意泛型,默认 Self
    type Output = Foo;
    fn mul(self, rhs: Foo) -> Self::Output {
        rhs
    }
}
fn main() {
    let foo1 = Foo{};
    let foo2 = Foo{};
    let foo3 = Foo{};
    let bar = Bar{};
    println!("{:?}", foo1 + foo2);
    println!("{:?}", bar * foo3);
}

Drop 接口

struct Droppable {
}
impl Drop for Droppable {
    fn drop(&mut self) {
        // 销毁时调用,释放资源。
        // 按照对象生成的相反顺序销毁,类似出栈
    }
}

迭代器接口

struct Fibonacci {
    curr: u32,
    next: u32,
}
impl Fibonacci {
    fn new() -> Fibonacci {
        Fibonacci { curr: 1, next: 1 }
    }
}
impl Iterator for Fibonacci {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
        let current = self.curr;
        self.curr = self.next;
        self.next = self.curr + current;
        Some(current)
    }
}

fn main() {
    let fib = Fibonacci::new();
    for i in fib.skip(2).take(5) {
        // for 循环中自动调用 .into_iter() 方法
        println!("> {}", i); // 2 3 5 8 13
    }
    for i in 0..3 {}
}

traits 作为输入输出

fn f<'a>(r : &'a impl Display) -> impl Iterator + 'a {
    // 直接使用 imp traits 省略泛型
}

复制

  • Copy 简单的按位拷贝
  • Clone 深拷贝
// 出现Copy时必须有Clone
#[derive(Debug, Clone, Copy)]
struct Unit;

// 可拷贝 Box 指针内部数据
#[derive(Clone, Debug)]
struct Pair(Box<i32>, Box<i32>);

接口组合

rust 中没有“继承”

trait Obj {}
trait Named: Obj {
    fn name(&self) -> String;
}
trait Animal {
    fn run(self);
}
trait Dog: Named + Animal {}
fn f(d: &dyn Dog) {
    d.name();
    d.run();
}

接口方法冲突

trait A {
    fn fun(&self);
}
trait B {
    fn fun(&self);
}

struct S;
impl A for S {
    fn fun(&self) {}
}
impl B for S {
    fn fun(&self) {}
}

fn main() {
    let s = S {};
    <S as A>::fun(&s);
    <S as B>::fun(&s);
}

错误处理

  • panic!(); 用于不可恢复错误
  • Option 用于接收缺少值的情况
  • Result 用于让调用者解决错误

panic

fn fun() {
    panic!("严重错误");
}

条件编译

fn drink(s: &str) {
    if cfg!(panic="abort"){
        println!("");
    } else {
        println!("");
    }
}
#[cfg(panic = "abort")]
fn ah(){ println!("xxx");}

#[cfg(not(panic="abort"))]
fn ah(){ println!("yyy");}

fn drink(){
    ah();
}

rustc  lemonade.rs -C panic=abort 指定编译条件

Option

pub enum Option<T> {
    /// No value.
    None,
    /// Some value of type `T`.
    Some(T),
}

 可使用 match 分支或 unwrap 解决

使用 ?

Option变量后接上一个 ? 如果是Some则得到内部值,否则终止函数并返回 None

map map_or map_or_else 函数式编程

简化 match 分支语句

option.map(move |v| {....});
option.map_or(0, move |v| {xxx; 1}); // 为None返回0,否则执行函数处理Some内部值
option.map_or_else(f1, f2);          // 都是闭包,为None和Some分别执行不同函数

and_then

Option内部如果是None 则直接返回None,否则执行传入的闭包,闭包会返回一个Option

此外还有 or() or_else() get_or_insert() get_or_insert_with()

Result

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

例如解析字符串失败时返回Result,同时 main 函数也可以返回 Result

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number_str = "10";
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };
    println!("{}", number);
    Ok(())
}

和 Option 类似,也可使用 match 处理。也有 map or and 等简化方法。也有和Option一样的 ? 操作。

可使用 type 关键字起别名

type AliasedResult<T> = Result<T, ParseIntError>;

常用技巧有 Result 和 Option 的相互嵌套,使用Box包装Result

 

测试

单元测试

测试单一模块,且能够测试私有接口。

创建 tests 模块并使用 #[cfg(test)] 注解。一般测试模块与目标函数放置于同一个文件。

使用 cargo test 运行测试,cargo test name_slice 可运行指定测试

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
// 私有方法也可测试
fn bad_add(a: i32, b: i32) -> i32 {
    a - b
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    #[ignore = "这个属性能用于忽略测试"]
    #[test]
    fn test_add() {
        assert_eq!(add(1, 2), 3);
    }

    // 测试函数也可以返回 Result
    #[test]
    fn test_bad_add() -> Result<(), String> {
        if 2 == bad_add(1, 1) {
            Ok(())
        } else {
            Err("the function is bad".to_owned())
        }
    }

    #[test]
    #[should_panic(expected="should panic")] // 当希望发生 panic 时使用
    fn test_should_panic() {
        panic!("should panic")
    }
}

文档测试

文档注解支持commonMarkdown格式,其中的代码块在 cargo test 时也执行。

集成测试

cargo将集成测试放在与src平级的tests目录中,只能使用公开接口。
可以将所有测试都需要的初始设置放入一个函数,再引入test

例如:

// tests/common/mod.rs
pub fn setup() {
    // some setup code, like creating required files/directories, starting
    // servers, etc.
}
// importing common module.
mod common;

#[test]
fn test_add() {
    // using common code.
    common::setup();
    assert_eq!(adder::add(3, 2), 5);
}

开发期依赖

 引入后只在开始时有效

# standard crate data is left out
[dev-dependencies]
pretty_assertions = "1"

Vector

可变长的数组,一个vector对象有3个组成部分:指向数据的指针,长度,容量。

fn main() {
    let mut v1: Vec<i32> = (0..10).collect();
    let mut v2 = vec!["a", "b", "c"];
    println!(
        "{} {} {} {}", // 10 10 3 3
        v1.len(),
        v1.capacity(),
        v2.len(),
        v2.capacity()
    );

    for (_i, x) in v1.iter_mut().enumerate() {
        *x *= 2;
    }

    v2.append(&mut vec!["d", "e", "f", "g"]);
    println!(
        "{} {} {:?}", // 7 7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
        v2.len(),
        v2.capacity(),
        v1
    );
}

字符串

Rust中有两种字符串:String 和 &str。String是存储在堆上的字节vector,是不以null结尾的utf8字符。&str是指向utf8序列的切片,指向String。

std::str 和 std::string 专门用于处理 str 和 string 类型。

fn main() {
    let s: &'static str = "this is a string data"; // 字面量分配在常量区,只读
    println!("{:p} : {}", s.as_ptr(), s);

    for word in s.split_whitespace().rev() {
        println!("{:p} : {}", word.as_ptr(), word);
    }

    let S = String::from("this is a string data");
    let ss = s.replace("string", "String");
    println!("{:p}", S.as_str());
    println!("{:p}", ss.as_str());
}
// print:
// 0x7ff6dbc656e8 : this is a string data
// 0x7ff6dbc656f9 : data
// 0x7ff6dbc656f2 : string
// 0x7ff6dbc656f0 : a
// 0x7ff6dbc656ed : is
// 0x7ff6dbc656e8 : this
// 0x2699c84f380
// 0x2699c8522c0

智能指针

Box

类似c++的 unique_ptr

智能指针,rust中对象默认分配栈上。可通过 Box 使其分配到堆上,当Box离开作用域时指针和内部对象都销毁。可以通过 * 操作符解引用,直接获得内部对象。

#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}
fn main() {
    let p = Point { x: 1.0, y: 2.0 };
    let bp = Box::new(p);
    println!(
        "{} {} {}", // 16 8 16
        std::mem::size_of_val(&p),
        std::mem::size_of_val(&bp),
        std::mem::size_of_val(&*bp)
    );
}

Rc

Rc(Reference Counting)是对内部对象的引用计数指针,类似c++的 shared_ptr 和 weak_ptr。

内部分为强引用和弱引用,弱引用用于防止循环引用。

fn main() {
    let s = "xxx".to_string();
    let s1 = Rc::new(s);
    println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
    {
        let _s2 = Rc::clone(&s1);
        println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 2 0
    }
    {
        println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
        let s3 = Rc::downgrade(&s1);
        println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 1
        let _s4 = s3.upgrade();
        println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 2 1
    }
    println!("{} {}", Rc::strong_count(&s1), Rc::weak_count(&s1)); // 1 0
}

Arc

Arc(Atomically Reference Counted)用于多线程间共享所有权。

use std::{sync::Arc, thread};

fn main() {
    let s = Arc::new("string");

    let mut join_vec = vec![];

    for _ in 0..10 {
        let ss = Arc::clone(&s);
        let join = thread::spawn(move || {
            println!("{:?} {:?} {:p}", ss, Arc::strong_count(&ss), ss.as_ptr());
        });
        join_vec.push(join);
    }
    join_vec.into_iter().for_each(|j| {
        let _ = j.join();
    });
}
// "string" 5 0x7ff7f8e29f38
// "string" 6 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 11 0x7ff7f8e29f38
// "string" 10 0x7ff7f8e29f38
// "string" 10 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 9 0x7ff7f8e29f38
// "string" 8 0x7ff7f8e29f38
// "string" 8 0x7ff7f8e29f38

多线程

 一个简单 map-reduce 程序

MapReduce
 use std::{collections::HashMap, thread};

fn main() {
    let data = "86967897737416471853297327050364959
11861322575564723963297542624962850    
70856234701860851907960690014725639
38397966707106094172783238747669219
52380795257888236525459303330302837
58495327135744041048897885734297812
69920216438980873548808413720956532
16278424637452589860345374828574668";
    let mut join_vec = vec![];

    let chunked_data = data.split_whitespace();

    for (i, segment) in chunked_data.enumerate() {
        join_vec.push(thread::spawn(move || -> HashMap<u32, i32> {
            let mut map = HashMap::new();

            segment.chars().for_each(|c| {
                let num = c.to_digit(10).expect("should be digit");

                match map.get_key_value(&num) {
                    Some((&k, &v)) => {map.insert(k, v + 1);}
                    None => {map.insert(num, 1);}
                }
            });
            println!("thread {i} get result: {:?}", map);
            map
        }));
    }
    let mut final_result = HashMap::new();
    join_vec.into_iter().for_each(|res| {
        match res.join() {
            Ok(map) => {
                map.iter().for_each(|(&k, &v)| {
                    match final_result.get(&k) {
                        Some(old_result) => {
                            final_result.insert(k, v + old_result);
                        },
                        None => {
                            final_result.insert(k, v);
                        },
                    }
                });
            },
            Err(err) => {
                println!("thread fail: {:?}", err);
            },
        }
    });
    println!("final result: {:#?}", final_result);
}

标准库的 channel 支持多sender,1个receiver。第三方库 crossbeam_channel 支持多个生产者和消费者。

use std::{sync::mpsc, thread};

static THREAD_NUM: i32 = 3;

fn main() {
    let (sender, receiver) = mpsc::channel();

    let mut join_vec = vec![];

    for i in 0..THREAD_NUM {
        let sender2 = sender.clone();
        let join = thread::spawn(move || {
            sender2.send(i).unwrap();
            println!("thread {i} send: {i}");
        });
        join_vec.push(join);
    }

    for _ in 0..THREAD_NUM {
        receiver.recv().map_or_else(
            |err| println!("err: {err}"),
            |recv| println!("recv: {recv}"),
        );
    }

    for j in join_vec {
        j.join().expect("the child thread panic");
    }
}

IO

 *nix 和 windows 内部使用不同的格式保存字符串,所以Path对应的路径表示不是uft8格式字符串,而是依赖平台的 OsStr。Path 依赖 OsStr 创建,是不可变的。PathBuf 是可变的,与 Path 相关的。关系类似String 和 str。Path 转换为字符串可能失败,但一定可转换为 OsStr。

use std::path::Path;

fn main() {
    let cur_path = Path::new(".");
    let mut new_path = cur_path.join("a").join("b");
    new_path.push("c");
    new_path.push("file.txt");

    println!("{:?} {:?}", cur_path, new_path);
}

文件操作:

use std::{
    fs::{self, File},
    io::{self, Write, BufRead},
    path::Path,
};

fn main() -> io::Result<()> {
    let file_path = Path::new("D:/tmp.txt");
    if file_path.try_exists()? {
        fs::remove_file(file_path)?;
    }

    {
        let mut f = File::create(file_path)?;
        f.write(b"123456\nabcdef")?;
    }

    let file = match File::open(file_path) {
        // 当 File 对象超出作用域时,自动释放资源
        Ok(file) => file,
        Err(err) => panic!("open file failed: {}", err),
    };

    // file.read_buf()
    for ele in io::BufReader::new(file).lines() {
        if let Ok(line) = ele {
            println!("{line}");
        }
    }

    Ok(())
}

 子进程

use std::{io, process::Command};

fn main() -> io::Result<()> {
    let output = Command::new("rustc").args(["--version"]).output()?;
    if output.status.success() {
        let s = String::from_utf8_lossy(&output.stdout);
        println!("successded:\n{}", s);
    } else {
        let s = String::from_utf8_lossy(&output.stderr);
        println!("fail:\n{}", s);
    }

    Ok(())
}
posted @ 2023-03-12 21:45  某某人8265  阅读(34)  评论(0编辑  收藏  举报