/*
参考文章:
https://zhuanlan.zhihu.com/p/112307245
https://rustcc.cn/article?id=76e5f3fb-20b9-48c9-8fc6-a0aad40ced8c
https://www.codercto.com/a/27950.html
https://www.twle.cn/c/yufei/rust/rust-basic-smart-pointers.html
https://www.cnblogs.com/dream397/p/14185003.html
1.引用和智能指针的一个的区别是引用是一类只借用数据的指针;相反,在大部分情况下,智能指针拥有他们指向的数据
2.它与普通数据结构的区别在于智能指针实现了Deref和Drop这两个traits。实现Deref可以使智能指针能够解引用,而实现Drop则使智能指针具有自动析构的能力。
Box<T>:用来在堆内存中保存数据使用的
Rc<T>:一个引用计数类型,其数据可以有多个所有者。
Ref<T> 和 RefMut<T>,通过RefCell<T>访问,一个在运行时而不是在编译时执行借用规则的类型。
*/
//测试栈区大小,经过测试大于1兆小于2兆,当栈申请大于2兆的时候编译没有问题,运行会出现fatal runtime erro:stack overflow
fn test_1() {
let c: [i32; 1 * 1024 * 1024];
}
//复习一下引用
//Rust中使用&符号表示引用,也叫引用操作符。其使用场景是只使用类型的值但不获取其所有权。同时Rust的引用规则为:
// 在作用域中的数据有且只能有一个可变引用;
// 可以有多个不可变引用;
// 不能同时拥有不可变引用和可变引用。
fn test_2() {
println!("----------------------test_2-----------------");
let adrr = 123;
let addr1 = &adrr; // 通过 & 得到引用,默认是不可变的
let addr2 = &adrr; // 通过 & 得到引用,默认是不可变的
println!("{}-{}", *addr1, *addr2); // 输出里面的值
println!("{}-{}", addr1, addr2); // 不带*号也可以的,这是因为编译器识别了这个类型,取出来了里面的值
println!("{:p}-{:p}", addr1, addr2); // 其实这个两个都是地址
//println!("{:p}", adrr); // 这样就不行
let mut vec = vec![1, 2, 3]; // 要获取可变引用,必须先声明可变绑定
let new_vec = &mut vec; // 通过 &mut 得到可变引用
//let new_vec2 = &mut vec; // 通过 &mut 得到可变引用
new_vec.push(40);
println!("{:?}", vec);
let new_vec2 = &mut vec; //不知道为什么在这里又可以再次引用了???
let mut str2 = String::from("world");
let r1 = &str2;
let r2 = &mut str2; //只声明没有问题,一旦有使用者就会编译报错
// println!("{}, {}, and", r1, r2); // ERROR:不能同时拥有不可变引用和可变引用
}
//研究特性Deref
//Deref就是用*取值的操作
fn test_3() {
println!("----------------------test_3-----------------");
let x = 5; // 值类型数据
let y = Box::new(x); // y 是一个智能指针,指向堆上存储的数据 5
println!("{}", 5 == x);
println!("{}", 5 == *y); // 为了访问 y 存储的具体数据,需要解引用
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
// 范型方法
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0 // 返回数据
}
}
let x = 5;
let y = MyBox::new(x); // 调用静态方法 new() 返回创建一个结构体实例
println!("5==x is {}", 5 == x);
println!("5==*y is {}", 5 == *y); // 解引用 y
println!("x==*y is {}", x == *y); // 解引用 y
}
//研究特性Drop
//Drop对于智能指针非常重要,它是在智能指针被丢弃时自动执行一些清理工作,这里所说的清理工作并不仅限于释放堆内存,还包括一些释放文件和网络连接等工作
fn test_4() {
println!("----------------------test_4-----------------");
use std::ops::Drop;
#[derive(Debug)]
struct S(i32);
impl Drop for S {
fn drop(&mut self) {
println!("drop {}", self.0);
}
}
let x = S(1);
println!("create x: {:?}", x);
{
let y = S(2);
println!("create y: {:?}", y);
}
//-------------------------
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> Drop for MyBox<T> {
fn drop(&mut self) {
println!("dropping MyBox object from memory ");
}
}
let x = 50;
MyBox::new(x);
MyBox::new("Hello");
}
//Box
//Box<T>是堆上分配的指针类型,称为“装箱”(boxed),其指针本身在栈,指向的数据在堆
//box关键字会调用Rust内部的exchange_malloc和box_free方法来管理内存
//Box使用场景:
//1.递归类型和trait对象。Rust需要在编译时知道一个类型占用多少空间,Box<T>的大小是已知的。
//2.“大”的数据转移所有权。用Box<T>只需拷贝指针。
// a.当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候;
// b.当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候;
// c.当希望拥有一个值并只关心它的类型是否实现了特定trait而不是具体类型的时候;
fn test_5() {
println!("----------------------test_5-----------------");
let x = Box::new("hello");
println!("{:?}", x.chars());
//递归类型的经典示例:
use List::{Cons, Nil};
/*
//无法编译通过的例子,原因时编译器无法算出他所需的大小
enum List{
Con(i32,List),
Nil
}
fn main(){
let list = Con(1,Con(2,Con(3,Nil)));
}
//改进
enum LIst{
Con(i32,Box<List>),
Nil
}
fn main(){
let list = Con(1,Box::new(Con(2,Box::new(3,Nil))));
}
*/
#[derive(Debug)]
enum List<T> {
Cons(T, Box<List<T>>),
Nil,
}
let recursive_list: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Nil))));
println!("{:?}", recursive_list); // 打印出:Cons(1, Cons(2, Nil))
//trait对象的示例:
trait T {
fn m(&self) -> u64;
}
struct S {
i: u64,
}
impl T for S {
fn m(&self) -> u64 {
self.i
}
}
fn f(x: Box<dyn T>) {
println!("{}", x.m())
}
let s = S { i: 100 };
println!("{}", s.m());
let b: Box<S> = Box::new(S { i: 100 });
f(b);
}
//Rc <T>引用计数智能指针 reference counting
//使用场景:当我们希望在堆上分配一些内存供程序的多个部分去读取,而且无法在编译时确定程序的哪一部分会最后结束使用它的时候。
//Rc<T>只能用于单线程场景。(多线程使用arc)
//特征:允许相同数据有多个所有者,在编译时执行不可变借用检查。
fn test_6(){
println!("----------------------test_6-----------------");
use std::rc::Rc;
enum List{
Con(i32,Rc<List>),
Nil
}
let a = Rc::new(List::Con(5,Rc::new(List::Con(10,Rc::new(List::Nil)))));
let b = List::Con(3,Rc::clone(&a));
let c = List::Con(4,Rc::clone(&a));
//打印引用计数,结果应该是3
println!("the Rc count is {}",Rc::strong_count(&a));
//--------------------------------------------------------
struct Owner {
name: String,
// ...other fields
}
struct Gadget {
id: i32,
owner: Rc<Owner>,
// ...other fields
}
let gadget_owner: Rc<Owner> = Rc::new( //堆分配Owner, 产生一个Rc<Owner>计数
Owner {
name: "Gadget Man".to_string(),
}
); //现在有一个Owner,在堆中,一个Rc<Owner>, 指针
let gadget1 = Gadget {
id: 1,
owner: Rc::clone(&gadget_owner), //获得一个指向堆中Owner的Rc<Owner>,计数加一
};
let gadget2 = Gadget {
id: 2,
owner: Rc::clone(&gadget_owner), //获得指针,计数加一
}; //现在有一个Owner, 三个Rc<Owner>
drop(gadget_owner); //std::mem::drop,销毁一个Rc<Owner>,内存Owner还在
//剩余两个Rc<Owner>仍然指向Owner
println!("Gadget {} owned by {}", gadget1.id, gadget1.owner.name);
println!("Gadget {} owned by {}", gadget2.id, gadget2.owner.name)
}
//RefCell <T>
//作用:RefCell<T>用于当你确信代码遵守借用规则,而编译器不能理解和确定的时候。
//特征:单一所有者,在运行时执行不可变或可变借用检查。
fn test_7(){
}
fn main() {
test_1();
test_2();
test_3();
test_4();
test_5();
test_6();
test_7();
}