指针类型
Rust中与指针相关类型有如下几种
- Reference
- shared reference: &T
- immutable reference: &mut T
- Raw Pointer
- * const T: 对应于&T
- * mut T: 对应于&mut T
- Smart Pointer
-
Box<T>
-
Rc<T>
-
...
-
- 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 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<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
使用场景:
- 编译时不确定大小的 动态数据结构(如递归实现的Recursive Type; stack上留下确定大小的pointer,heap上运行时分配内存)
- 大量数据或大型数据结构,(stack的容量有限且珍贵),保证在ownership的转移时不作额外的复制操作
//Box<T>的定义 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