NiceLeeのBlog 用爱发电 bilibili~

Rust 入门时容易迷惑的技术细节(trait篇)

2022-11-27
nIceLee

阅读:


在入门的时候并不能全方位地看待问题,有时候会忽略一些显而易见的知识,现在回过头梳理,把前面的补起来。

let ident = expr;发生的是复制还是移动?

  • 当赋值时,所有实现了Copy trait的赋值会发生复制,它会复制一份在栈上的值。
  • 当赋值时,其它情况会发生转移。这时栈上会复制一份指针,但指向的堆上的实际值不变,会发生所有权的转移。
  • 如果某个类型或它的任意一部分实现了Drop trait,那么就不能再实现Copy trait。

我们来看一个新手很容易碰到的问题:

pub fn copy_test() {
    // 复制
    let num1: i32 = 666;
    let num2: i32 = num1;
    println!("{}", num1);
    println!("{}", num2);
}

    
pub fn move_test() {
    // 移动
    #[derive(Debug)]
    struct Point(i32, i32);
    let p1 = Point(0, 0);
    let p2 = p1;
    // println!("{:?}", p1); // 这一句去掉注释会报错
    println!("{:?}", p2);
}

为了解决以上问题,有三种常见的解决办法。

  • 使用引用
pub fn ref_test() {
    // 移动
    #[derive(Debug)]
    struct Point(i32, i32);
    let p1 = &Point(0, 0);
    let p2 = &p1;
    println!("{:?}", p1); // 
    println!("{:?}", p2);
}
  • 克隆该值再赋值
pub fn clone_test() {
    #[derive(Debug,Clone)]
    struct Point(i32, i32);
    let p1 = Point(0, 0);
    let p2 = p1.clone();
    println!("{:?}", p1); // 
    println!("{:?}", p2);
}
  • 为该类型实现Copy trait
pub fn move_copy_test() {
    struct Point(i32, i32);

    impl std::fmt::Display for Point {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.write_fmt(format_args!("Point:({}, {})", self.0, self.1))
        }
    }

    impl Clone for Point {
        fn clone(&self) -> Self {
            Self(self.0.clone(), self.1.clone())
        }
    }

    impl Copy for Point {}
    let p1 = Point(1, 1);
    let p2 = p1;
    println!("p1: {}", p1); // 不实现 Copy trait 此处会报错
    println!("p2: {}", p2);
}

为类型实现+==等操作

这个比较简单,实现相应的trait即可。

pub fn implement_symbol_add_eq_trait() {
    // #[derive(Debug)]
    struct Point {
        x: i32,
        y: i32,
    }
    impl std::fmt::Debug for Point {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.debug_struct("Point")
                .field("x", &self.x)
                .field("y", &self.y)
                .finish()
        }
    }
    impl PartialEq<Point> for Point {
        fn eq(&self, other: &Point) -> bool {
            self.x == other.x && self.y == other.y
        }
    }

    impl std::ops::Add<&Point> for Point {
        type Output = Self;

        fn add(self, other: &Point) -> Self {
            Self {
                x: self.x + other.x,
                y: self.y + other.y,
            }
        }
    }
    let p1 = Point { x: 1, y: 1 };
    let p2 = Point { x: 1, y: 1 };
    println!("p1 == p2: {}", p1 == p2);
    println!("p1 + &p2: {:?}", p1 + &p2);
}

A trait相关的的类型实现B trait

举个例子,类型A实现了From<i32> trait,这个trait干什么呢?
它有个从i32产生的“构造函数”TypeA::from(i32)
那么我们很可能有另外一种into写法,如下:

let ta: TypeA = TypeA::from(666);
let ta: TypeA = 666.into();

这是怎么实现的呢?

pub fn implement_into_for_from_trait() {
    trait From<T>: Sized {
        fn from0(val: T) -> Self;
    }

    trait Into<T>: Sized {
        fn into0(&self) -> T;
    }
    // 假设T1实现了From<T2>, 且T2实现了Clone
    // 我们为T2实现Into<T1>
    impl<T1: From<T2>, T2: Clone> Into<T1> for T2 {
        fn into0(&self) -> T1 {
            T1::from0(self.clone())
        }
    }

    #[derive(Debug)]
    struct TypeA(i32);
    // TypeA实现了From<i32>
    // 于是i32自动实现了Into<TypeA>
    impl From<i32> for TypeA {
        fn from0(val: i32) -> Self {
            TypeA(val)
        }
    }
    let ta: TypeA = TypeA::from0(666);
    println!("{:?}", ta);
    let ta: TypeA = 666.into0();
    println!("{:?}", ta);
}

内容
隐藏