rust变量绑定、拷贝、转移、引用、指针

Rust语言Clone、指针等特性详解
该博客围绕Rust语言展开,详细介绍了Clone、Copy特征,变量绑定与赋值的不同场景,引用和Borrow的使用规则及生命周期。还深入剖析了多种智能指针,如NonNull、UnsafeCell等的含义、用法和源码,以及指针的搭配使用和分类,最后提及了ref的相关内容。

目录

一,Clone、Copy

1,基本类型

2,类型的Clone特征

3,显式声明结构体的Clone特征

4,类型的Copy特征

5,显式声明结构体的Copy特征

5,变量和字面量的特征

6,特征总结

二,变量绑定、赋值

1,变量绑定:Clone拷贝场景

2,变量绑定:Copy拷贝场景

3,变量绑定:所有权转移场景

4,转移的永久性

5,赋值语句

三,引用、Borrow

1,对常量的引用

2,对变量的不可变引用

3,对变量的可变引用

4,函数调用

5,Borrow、BorrowMut

四,引用总结

1,引用的生命周期

2,对字面量的引用

3,对普通变量的引用

4,对引用变量的引用

5,对同一变量的引用

6,链式引用

五,智能指针

0,相关通用知识

(1)deref

(2)智能指针嵌套

(3)隐式转换导致拥有多个同名函数

1,NonNull

(1)一句话含义

(2)源码解析

2,PhantomData

(1)一句话含义

(2)源码解析

3,UnsafeCell

(1)一句话含义

(2)具体含义

(3)用法示例

(4)安全性

(5)源码解析

4,Atomic*、内部可变性

5,Cell

(1)一句话含义

(2)用法示例

(3)get方法

(4)源码解析

6,RefCell

(1)一句话含义

(2)用法示例

(3)源码解析

7,Mutex

(1)一句话含义

(2)源码解析

8,RwLock

(1)一句话含义

(2)具体含义

(3)源码解析

9,Arc

(1)一句话含义

(2)源码解析

10,RcBox

(1)一句话含义

(2)源码解析

11,Rc

(1)一句话含义

(2)用法

(3)Rc的borrow

(4)源码解析

12,UniqueRc

(1)源码解析

13,Box

(1)源码解析

14,ThinBox

(1)源码解析

六,智能指针搭配使用

1,Arc+Mutex

2,Arc+RwLock

七,指针总结

1,引用

2,胖指针

3,智能指针

4,原始指针、unsafe块

八,ref


一,Clone、Copy

1,基本类型

rust基本类型包括:

  • 所有整数类型,比如 u32
  • 布尔类型,bool,它的值是 true 和 false
  • 所有浮点数类型,比如 f64
  • 字符类型,char

2,类型的Clone特征

拥有Clone特征的类型:

  • 基本类型
  • String
  • 容器
  • 显式声明Clone特征的结构体

没有Clone特征的类型:

  • 没有显式声明Clone特征的结构体(结构体默认)

递归决定是否有Clone特征的类型:

  • 元组,当且仅当其包含的类型都有Clone特征的情况下,其自身有Clone特征。

3,显式声明结构体的Clone特征

显式声明结构体的Clone特征的前提条件:当且仅当结构体中的成员都具有Clone特征的情况下,可以显式声明Clone特征。

#[derive(Clone)]
struct S{}

#[derive(Clone)]
struct P{
    a:i32,
    b:S,
}

4,类型的Copy特征

拥有Copy特征的类型:

  • 基本类型
  • 显式声明Copy特征的结构体

没有Copy特征的类型:

  • String
  • 容器
  • 没有显式声明Copy特征的结构体(结构体默认)

递归决定是否有Copy特征的类型:

  • 元组,当且仅当其包含的类型都有Copy特征的情况下,其自身有Copy特征。

5,显式声明结构体的Copy特征

一定是具有Clone特征的类型,才可能具有Copy:

pub trait Copy: Clone {
    // Empty.
}

显式声明结构体的Copy特征的前提条件:当且仅当结构体中的成员都具有Copy特征的情况下,可以显式声明Copy特征。

#[derive(Clone,Copy)]
struct P{
    a:i32,
}

fn main() {
    let x:P=P{a:5};
    let y=x;
    assert_eq!(x.a,5);
}

5,变量和字面量的特征

字面量会自动推导出类型,所以变量和字面量都有唯一确定的类型。

变量和字面量是否具有clone和copy特征,完全取决于其类型是否具有。

6,特征总结

所有类型可以分为3类:

没有clone和copy特征,有clone没有copy特征,有clone和copy特征。

二,变量绑定、赋值

1,变量绑定:Clone拷贝场景

对于有clone特征的变量或字面量,可以调用clone函数进行拷贝而不转移所有权。

#[derive(Clone)]
struct P{
    a:i32,
}

fn main() {
    let x:P=P{a:5};
    let y=x.clone();
    assert_eq!(x.a,5);
}

2,变量绑定:Copy拷贝场景

如果let绑定语句的等号右边是一个有Copy特征的变量或字面量,那么这是一个拷贝行为。

    let x = 5;
    let xx = x;
    assert_eq!(5, x);
    assert_eq!(6, xx+1);

3,变量绑定:所有权转移场景

如果let绑定语句的等号右边是一个没有Copy特征的变量或字面量,那么这是一个所有权转移的行为。

错误代码:

    let x = vec![1,2,3];
    assert_eq!(x[0],1);
    let y=x;
    assert_eq!(x[0],1); // 错误

错误原因:y转移走了所有权,不能再使用x

4,转移的永久性

错误代码:

fn f(){
    let x = "he".to_string();
    if false{
        let y = x;
    }
    println!("{}",x);
}

错误原因:y转移了x的所有权之后,x就再也不能用了,即使y的生命周期结束了也一样。

即使这里用if false包起来了,只要这部分代码能编译,y就转移走x的所有权。

5,赋值语句

无论是let语句,还是赋值语句,有Copy的就会Copy,没有Copy的就转移所有权

PS:等号右边是形如x.clone()的,虽然不会转移x本身的所有权,但是会转移走clone函数返回值的所有权。

struct S{
    x:i32
}
fn main() {
    let c= S{x:1};
    let mut d =S{x:2};
    d=c;
    assert_eq!(d.x, 1);

    let mut x = Vec::from([1,2,3]);
    let mut y = Vec::from([1,2,4]);
    y=x;
    assert_eq!(y[2], 3);

    let x = 5;
    let mut y = 6;
    y=x;
    assert_eq!(x, 5);
    assert_eq!(y, 5);
}

三,引用、Borrow

1,对常量的引用

fn main() {
    let x:P=P{a:6};
    let y= & x;
    assert_eq!(x.a,6);
    assert_eq!(y.a,6);
    assert_eq!((*y).a,6);
    println!("end");
}

常量只有可读性,原变量x和引用变量y都持有读的能力。

这里y可以直接用,也可以先解引用再用。

2,对变量的不可变引用

正确代码:

struct P{
    a:i32,
}
fn main() {
    let mut x:P=P{a:6};
    let y= &x;
    assert_eq!(x.a,6);
    assert_eq!(y.a,6);
    assert_eq!((*y).a,6);
    x.a=5;
    assert_eq!(x.a,5);
    println!("end");
}

变量x持有读写能力,不可变的引用y只有读能力。

错误代码:

struct P{
    a:i32,
}
fn main() {
    let mut x:P=P{a:6};
    let y= &x;
    x.a=5;
    if false{
        assert_eq!(y.a,5);
    }
    println!("end");
}

错误原因:在y的读行为结束之前,x不能执行写行为,否则会造成冲突。即使后面的y的读写包在了if false里面也是一样,因为编译器不做这种代码运行推导。

同一个变量可以引用多次,也可以对引用变量再进行引用:

struct P{
    a:i32,
}
fn main() {
    let mut x:P=P{a:6};
    let y= &x;
    let z=&x;
    let z2=&z;
    let z3=&z2;
    let z4=&z3;
    assert_eq!(x.a,6);
    assert_eq!(y.a,6);
    assert_eq!(z.a,6);
    assert_eq!(z4.a,6);
    assert_eq!((*z4).a,6);
    assert_eq!((**z4).a,6);
    assert_eq!((***z4).a,6);
    assert_eq!((****z4).a,6);
    println!("end");
}

这里的z4可以直接读成员,也可以解引用若干次再使用。

3,对变量的可变引用

正确代码:

struct P{
    a:i32,
}
fn main() {
    let mut x:P=P{a:6};
    let y= &mut x;
    assert_eq!(y.a,6);
    y.a=5;
    assert_eq!(x.a,5);
    println!("end");
}

错误代码:

struct P{
    a:i32,
}
fn main() {
    let mut x:P=P{a:6};
    let y= &mut x;
    assert_eq!(x.a,6);
    assert_eq!(y.a,6);    
    println!("end");
}

错误原因:y的读写行为结束之前,x不能执行读行为,否则会造成冲突

可变引用和不可变引用不能同时存在,否则会造成冲突。

4,函数调用

错误代码:

fn fun(x:Vec<i32>)->i32{
    x[0]+1
}

fn main() {
    let x = vec![1,2,3];
    assert_eq!(fun(x),2);
    assert_eq!(x.len(), 3);
    println!("end");
}

错误原因:函数调用时转移走了所有权。

正确代码:

fn fun(x:&Vec<i32>)->i32{
    x[0]+1
}

fn main() {
    let x = vec![1,2,3];
    assert_eq!(fun(&x),2);
    assert_eq!(x.len(), 3);
    println!("end");
}

实现方式:函数入参改成引用类型,传参时也要改成引用。

5,Borrow、BorrowMut

泛型的Borrow特征比单纯的引用更加强大,比如可以引用数据内的部分数据,或者和指针相关的操作。

pub trait Borrow<Borrowed: ?Sized> {
    #[stable(feature = "rust1", since = "1.0.0")]
    fn borrow(&self) -> &Borrowed;
}

pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> {
    #[sta
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值