rus语法 2

泛型

接口约束也可以是空接口

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

posted @ 2023-03-16 21:11  某某人8265  阅读(22)  评论(0编辑  收藏  举报