Rust: 引用与指针类型

指针类型

Rust中与指针相关类型有如下几种

  • Reference
    • shared reference: &T
    • immutable reference: &mut T
  • Raw Pointer
    • * const T:    对应于&T
    • * mut T:       对应于&mut T
  • Smart Pointer
    • Box
    • Rc
    • ...
  • Function Pointer

Reference

引用类型,primitive type 的一种,表示从某个拥有(借用到)所有权的`变量`借用它的值

//定义可变引用
    let mut x=5;
    let mut_ref=&mut x; //等价于 let ref mut mut_ref=x;
    let mut_ref2=&mut_ref;
    
//定义共享引用
    let y:i32=100;
    let shared_ref=&y;//等价于 let ref shared_ref =y;
    let shared_ref2=&shared_ref;
    
//解引用
    println!("value:y={},shared_ref={},shared_ref2={}",y,shared_ref,shared_ref2);//自动解引用
    println!("value:y={},*shared_ref={},**shared_ref2={}",y,*shared_ref,**shared_ref2);//显式解引用
    //println!("***shared_ref2={}",***shared_ref2);//错误的解引用:(type `i32` cannot be dereferenced)

//引用之间的关系
    println!("address:y={:p},shared_ref={:p},shared_ref2{:p}",&y,&shared_ref,&shared_ref2);
    println!("point to:shared_ref=>{:p},shared_ref2=>{:p}",shared_ref,shared_ref2);
    assert!(ptr::eq(&y,shared_ref));
    assert!(ptr::eq(&shared_ref,shared_ref2));

结果:

value:y=100,shared_ref=100,shared_ref2=100
value:y=100,*shared_ref=100,**shared_ref2=100
address:y=0x1c9daffa3c,shared_ref=0x1c9daffa40,shared_ref2=0x1c9daffa48
point to:shared_ref=>0x1c9daffa3c,shared_ref2=>0x1c9daffa40

Process finished with exit code 0

mut_ref2与shared_ref2说明 reference不但可以从拥有ownership的 变量 上获取 还可以从 借用到ownership的引用上获取

解引用:在The Rust Programing Languadge 中称为Implicit Deref Coercions,隐式强制解引用, 当传递一个引用作为function/method的argument又不与parameter type 不相匹配自动发生

所以无论几重引用,在关注值时不在需要添加复杂的&与*的运算,简化了代码. 同时,reference作为安全的指针,当发生了错误的解引用时会发生错误.这些相关特性都是在compile time 发生的.


&为取地址符号,即取引用,这个过程可以成为借用

*为解引用符号,当Rust的自动解引用机制使得 大多数时候省略该符号

{:p} 即实现了fmt::Pointer 的type可以通过这种方式以 指针形式 输出(变量在内存的 地址),这时不会存在自动解引用

共享引用与可变引用不能同时使用(即保证对同一value的借用时,所有共享引用的使用在可变引用之后)

Raw Pointer

即原生指针,裸指针,同c语言的指针相似.

在 The rust reference 中描述为:Raw pointers are pointers without safety or liveness guarantees.  

    let mut a=9;
    let mut b=8;
    let mut c=7;
//*mut T 的定义
    let mut_raw_p=&a  as *const i32 as *mut i32;
        /*
        //与下列方式等价
        let mut_raw_p:*mut i32=&a as *const i32 as *mut i32;
        let mut_raw_p=&mut a as *mut i32;
        let mut_raw_p:*mut i32=&mut a;
         */
//*const T 的定义
    let const_raw_p=&a as *const i32;
    let const_raw_p2:*const i32=&a;

//raw pointer的使用
    unsafe {
        //raw pointer 的解引用操作和相关函数都时unsafe的
        let pb=&b as *const i32;
        println!("&a={:p},&b={:p},&c={:p}",&a,&b,&c);

        //offset(),add()等函数
        println!("address:a={:p},b={:p},c={:p},",pb.offset(-1),pb.offset(0),pb.add(1));

        //raw pointer 不具有自动解引用作用
        println!("value:a={},b={},c={},",*pb.offset(-1),*pb.offset(0),*pb.add(1));

        // 指针越界
        println!("value:p1={},p2={},p3={},",*pb.offset(-10),*pb.offset(10),*pb.add(100));
    }

结果:

&a=0x8ed6ff514,&b=0x8ed6ff518,&c=0x8ed6ff51c
address:a=0x8ed6ff514,b=0x8ed6ff518,c=0x8ed6ff51c,
value:a=9,b=8,c=7,
value:p1=184832304,p2=-311429808,p3=-311429864,

原生指针类似C/C++的指针,不能自动解引用,也没有安全检查,需要unsafe 包围

使用相关函数add(),offset()等实现加减,而非直接使用+ ,-(error[E0369]: cannot add `*const i32` to `*const i32`)

Smart Pointer

即智能指针,在The Rust Programming Language 中描述为:

Smart pointers , are data structures that not only act
like a pointer but also have additional metadata and capabilities .
表现为指针,但不是指针,而是数据结构,并且拥有比指针更多的数据与能力
  • smart pointers implement the Deref and Drop traits

    • Deref trait 使得 该数据结构(struct) 可以像 引用一样使用,即自定义实现解引用操作,(Rust substitutes the * operator with a call to the deref method )
    • Drop trait 允许自定义 该数据结构(struct) 实例 out of scope时的行为,通过这种方式,编译器自动添加代码达到释放内存回收资源的目的
  • references  only borrow data; in contrast, in many cases, smart pointers own the data they point to.

Box

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

使用场景:

  • 编译时不确定大小的 动态数据结构(如递归实现的Recursive Typestack上留下确定大小的pointer,heap上运行时分配内存)
  • 大量数据或大型数据结构,(stack的容量有限且珍贵),保证在ownership的转移时不作额外的复制操作
​//Box的定义
    let stack_begin:usize=1;
    let x=Box::new(1);
    let y=Box::new([99;1024]);
    let z=Box::new(-1);
    let a :[usize;4]=[1,2,3,4];
    let stack_end:usize=2;

//act like a reference:自动解引用,显式解引用
    assert_eq!(*y,*(y.deref()));// Rust解引用的实质就是调用 deref()方法
    println!("value:x={},y={},*x={},*y={}",x,y[3],*x,(*(y.deref()))[3]);

//在heap中存储大量数据
    println!("address:stack_begin={:p},x={:p},y={:p},z={:p},a={:p},stack_end={:p}",&stack_begin,&x,&y,&z,&a,&stack_end);

//在heap中存储未知大小的数据:参考https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes

结果:

value:x=1,y=99,*x=1,*y=99
address:stack_begin=0x8d7b10e7b8,x=0x8d7b10e7c0,y=0x8d7b10e7c8,z=0x8d7b10f7d0,a=0x8d7b10f7d8,stack_end=0x8d7b10f7f8

用Box::new() 包裹let 语句发等号右边的表达式,即可实现内存分配在heap上

从打印出的地址关系可以看出,Box中的数据,无论其数据结构 在stack中只占用了一个指针大小即8Byte,64bit的内存空间,即使数据大小远超这个值

Box可以作为一种indirection,解决在compile time无法确定动态数据结构大小的问题;事实上所有动态数据结构都使用这一原理(如 String)

  • 匹配 实现某种trairt 的type 而非一个具体的type :? TODO

 

 

 

 

 

 

 

 

你可能感兴趣的:(Rust,Study,指针,rust,引用传递)