zoukankan      html  css  js  c++  java
  • 4.4 rust Smart Pointers Box and Deref 及 空针指

     Box

    Box是一个指针,具有固定长度,

    指针在栈上,指针指向的数据在堆上,

    这个复合类型是rust为用户提供的,可以实现递归调用的一个类型,它不会提升性能,

    所以,除了递归,一般不用这个.

    The most straightforward smart pointer is a box, whose type is written Box<T>. Boxes allow you to store data on the heap rather than the stack. What remains on the stack is the pointer to the heap data.

    Boxes don’t have performance overhead, other than storing their data on the heap instead of on the stack. But they don’t have many extra capabilities either. You’ll use them most often in these situations:

    • When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size
    • When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
    • When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type
    pub fn test() {
        let b = Box::new(5);
        println!("b = {}", b);
    }

    The deallocation happens for the box (stored on the stack) and the data it points to (stored on the heap).

    Enabling Recursive Types with Boxes

    At compile time, Rust needs to know how much space a type takes up. One type whose size can’t be known at compile time is a recursive type, where a value can have as part of itself another value of the same type.

    递归调用小例子

    Let’s explore the cons list, which is a data type common in functional programming languages, as an example of a recursive type. The cons list type we’ll define is straightforward except for the recursion;

    Each item in a cons list contains two elements: the value of the current item and the next item. The last item in the list contains only a value called Nil without a next item. A cons list is produced by recursively calling the cons function.

    常规递归写法,但在rust中行不通

    enum List {
        Cons(i32, List),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    pub fn test2() {
        let list = Cons(1, Cons(2, Cons(3, Nil)));
    }

    Because a Box<T> is a pointer, Rust always knows how much space a Box<T> needs: a pointer’s size doesn’t change based on the amount of data it’s pointing to. This means we can put a Box<T> inside the Cons variant instead of another List value directly. The Box<T> will point to the next List value that will be on the heap rather than inside the Cons variant. Conceptually, we still have a list, created with lists “holding” other lists, but this implementation is now more like placing the items next to one another rather than inside one another.

    #[derive(Debug, Clone)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }
    
    use List::{Cons, Nil};
    
    pub fn test2() {
        let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
        println!("{:?}",list)
    }

    输出

    Cons(1, Cons(2, Cons(3, Nil)))

    Boxes provide only the indirection and heap allocation; they don’t have any other special capabilities, like those we’ll see with the other smart pointer types. They also don’t have any performance overhead that these special capabilities incur, so they can be useful in cases like the cons list where the indirection is the only feature we need.

    The Box<T> type is a smart pointer because it implements the Deref trait, which allows Box<T> values to be treated like references. When a Box<T> value goes out of scope, the heap data that the box is pointing to is cleaned up as well because of the Drop trait implementation. Let’s explore these two traits in more detail.

    pub fn test3() {
        let x = 5;
        let y = Box::new(x);
    
        assert_eq!(5, x);
        assert_eq!(5, *y);
    }

    here we set y to be an instance of a box pointing to a copied value of x rather than a reference pointing to the value of x

    Deref

    通过实现Deref, 可以实现 从 地址中取出内容 的功能,就是 引用 相关的功能

    The MyBox<T> type is ultimately defined as a tuple struct with one element

    struct MyBox<T>(T);
    
    impl<T> MyBox<T> {
        fn new(x: T) -> MyBox<T> {
            MyBox(x)
        }
    }

    to implement a trait, we need to provide implementations for the trait’s required methods. The Deref trait, provided by the standard library, requires us to implement one method named deref that borrows self and returns a reference to the inner data

    struct MyBox<T>(T);
    
    impl<T> MyBox<T> {
        fn new(x: T) -> MyBox<T> {
            MyBox(x)
        }
    }
    use std::ops::Deref;
    
    impl<T> Deref for MyBox<T> {
        type Target = T;
    
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    
    pub fn test4() {
        let x = 5;
        let y = MyBox::new(x);
    
        assert_eq!(5, x);
        assert_eq!(5, *y);
    }

    We fill in the body of the deref method with &self.0 so deref returns a reference to the value we want to access with the * operator

    Without the Deref trait, the compiler can only dereference & references. The deref method gives the compiler the ability to take a value of any type that implements Deref and call the deref method to get a & reference that it knows how to dereference.

    Implicit Deref Coercions with Functions and Methods

    Deref coercion is a convenience that Rust performs on arguments to functions and methods. Deref coercion works only on types that implement the Deref trait. Deref coercion converts such a type into a reference to another type.

    For example, deref coercion can convert &String to &str because String implements the Deref trait such that it returns &str.

    Deref coercion happens automatically when we pass a reference to a particular type’s value as an argument to a function or method that doesn’t match the parameter type in the function or method definition. A sequence of calls to the deref method converts the type we provided into the type the parameter needs.

    实现了Deref,可以在类型转换,方法参数传递时,具有自动转换的功能,

    &String可以自动转为&str,就是因为String实现了Deref

    fn hello(name: &str) {
        println!("Hello, {}!", name);
    }
    
    pub fn test5() {
        let m = MyBox::new(String::from("Rust"));
        hello(&m);
    }

    Deref 可以实现 引用&, 反引用*, 它是一个抽象的类型,通常其他数据类型会实现它,所以日常编码中不常见

    还有隐式转换,

    隐式嘛,自动完成的,所以在使用的过程中,感受不明显

    实现Deref 可以实现 引用 的功能,但无法实现递归调用,要递归,还得用Box

    #[derive(Debug, Clone)]
    enum List2 {
        Data_List2(i32,Box<List2>),
        Nil_List2,
    }
    
    use List2::{Data_List2, Nil_List2};
    
    
    pub fn test8() {
        let first = Box::new(Nil_List2);
        let sec = Data_List2(2,first);
        let trd = Data_List2(3,Box::new(sec));
        let four = Data_List2(4,Box::new(trd));
        println!("{:?}",four);
    }

    输出

    Data_List2(4, Data_List2(3, Data_List2(2, Nil_List2)))

    变相空指针

    数据结构与算法的列表或链表,所描述的语言中通常有一个空指针,但rust中没有空指针的概念,可以通过unsafe的方式弄出来一个近似的,但这里有更简单的方式

    rust的意思是,空指针本质上就是一个特定的或者说是特殊的值,想要的话,直接用枚举定义一个就好了,并且还能判断是什么类型的空指针,没必要为所有的数据类型定义一个空指针...

    就是使用 枚举+Box,定义一个Null,再把数据节点放进去,让它们变成一个相同的类型...相同的枚举类型

    #[derive(Debug, Clone)]
    struct MyData {
        data: i32,
        next: Box<MyNode>
    }
    
    #[derive(Debug, Clone)]
    enum MyNode {
        Node(Box<MyData>),
        Null
    }
    
    
    pub fn test11() {
        let first_data = MyData{
            data: 1,
            next: Box::new(MyNode::Null)
        };
    
        let second_data = MyData{
            data:2,
            next:Box::new(MyNode::Node(Box::new(first_data)))
        };
    
        let three_data = MyData{
            data:3,
            next:Box::new(MyNode::Node(Box::new(second_data)))
        };
    
        println!("{:?}",three_data);
    }

    输出

    MyData { data: 3, next: Node(MyData { data: 2, next: Node(MyData { data: 1, next: Null }) }) }

    Running Code on Cleanup with the Drop Trait

    The second trait important to the smart pointer pattern is Drop, which lets you customize what happens when a value is about to go out of scope. You can provide an implementation for the Drop trait on any type, and the code you specify can be used to release resources like files or network connections. We’re introducing Drop in the context of smart pointers because the functionality of the Drop trait is almost always used when implementing a smart pointer. For example, when a Box<T> is dropped it will deallocate the space on the heap that the box points to.

    struct CustomSmartPointer {
        data: String,
    }
    
    impl Drop for CustomSmartPointer {
        fn drop(&mut self) {
            println!("Dropping CustomSmartPointer with data `{}`!", self.data);
        }
    }
    
    pub fn test() {
        let c = CustomSmartPointer {
            data: String::from("my stuff"),
        };
        let d = CustomSmartPointer {
            data: String::from("other stuff"),
        };
        println!("CustomSmartPointers created.");
    }
    CustomSmartPointers created.
    Dropping CustomSmartPointer with data `other stuff`!
    Dropping CustomSmartPointer with data `my stuff`!

    Dropping a Value Early with std::mem::drop

    use std::mem::drop;
    
    
    struct CustomSmartPointer2 {
        data: String,
    }
    
    impl Drop for CustomSmartPointer2 {
        fn drop(&mut self) {
            println!("Dropping CustomSmartPointer2 with data `{}`!", self.data);
        }
    }
    
    pub fn test2() {
        let c = CustomSmartPointer2 {
            data: String::from("some data"),
        };
        println!("CustomSmartPointer2 created.");
        drop(c);
        println!("CustomSmartPointer2 dropped before the end of main.");
    }

    输出

    CustomSmartPointer2 created.
    Dropping CustomSmartPointer2 with data `some data`!
    CustomSmartPointer2 dropped before the end of main.
  • 相关阅读:
    「题解」洛谷 P1169 [ZJOI2007]棋盘制作
    「题解」洛谷 P4147 玉蟾宫
    dsu on tree 学习笔记
    [USACO08FEB]Hotel G「线段树」
    城市环路「树形DP」
    Siano「线段树」
    Emiya 家今天的饭「容斥+DP」
    Matlab调用其他文件夹下的函数
    基于小波金字塔的简单图像融合算法matlab实现
    知网引文网络使用方法
  • 原文地址:https://www.cnblogs.com/perfei/p/15732683.html
Copyright © 2011-2022 走看看