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


    目录

    一,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,Cell

    (1)一句话含义

    (2)用法示例

    (3)源码解析

    5,Mutex

    (1)一句话含义

    (2)源码解析

    6,RwLock

    (1)一句话含义

    (2)具体含义

    (3)源码解析

    7,Arc

    (1)一句话含义

    (2)源码解析

    8,RcBox

    9,Rc

    (1)一句话含义

    (2)用法

    (3)Rc的borrow

    (4)源码解析

    10,Unique

    11,Box

    12,RefCell

    六,智能指针搭配使用

    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特征。

    1. #[derive(Clone)]
    2. struct S{}
    3. #[derive(Clone)]
    4. struct P{
    5. a:i32,
    6. b:S,
    7. }

    4,类型的Copy特征

    拥有Copy特征的类型:

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

    没有Copy特征的类型:

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

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

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

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

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

    1. pub trait Copy: Clone {
    2. // Empty.
    3. }

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

    1. #[derive(Clone,Copy)]
    2. struct P{
    3. a:i32,
    4. }
    5. fn main() {
    6. let x:P=P{a:5};
    7. let y=x;
    8. assert_eq!(x.a,5);
    9. }

    5,变量和字面量的特征

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

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

    6,特征总结

    所有类型可以分为3类:

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

    二,变量绑定、赋值

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

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

    1. #[derive(Clone)]
    2. struct P{
    3. a:i32,
    4. }
    5. fn main() {
    6. let x:P=P{a:5};
    7. let y=x.clone();
    8. assert_eq!(x.a,5);
    9. }

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

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

    1. let x = 5;
    2. let xx = x;
    3. assert_eq!(5, x);
    4. assert_eq!(6, xx+1);

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

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

    错误代码:

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

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

    4,转移的永久性

    错误代码:

    1. fn f(){
    2. let x = "he".to_string();
    3. if false{
    4. let y = x;
    5. }
    6. println!("{}",x);
    7. }

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

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

    5,赋值语句

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

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

    1. struct S{
    2. x:i32
    3. }
    4. fn main() {
    5. let c= S{x:1};
    6. let mut d =S{x:2};
    7. d=c;
    8. assert_eq!(d.x, 1);
    9. let mut x = Vec::from([1,2,3]);
    10. let mut y = Vec::from([1,2,4]);
    11. y=x;
    12. assert_eq!(y[2], 3);
    13. let x = 5;
    14. let mut y = 6;
    15. y=x;
    16. assert_eq!(x, 5);
    17. assert_eq!(y, 5);
    18. }

    三,引用、Borrow

    1,对常量的引用

    1. fn main() {
    2. let x:P=P{a:6};
    3. let y= & x;
    4. assert_eq!(x.a,6);
    5. assert_eq!(y.a,6);
    6. assert_eq!((*y).a,6);
    7. println!("end");
    8. }

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

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

    2,对变量的不可变引用

    正确代码:

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let mut x:P=P{a:6};
    6. let y= &x;
    7. assert_eq!(x.a,6);
    8. assert_eq!(y.a,6);
    9. assert_eq!((*y).a,6);
    10. x.a=5;
    11. assert_eq!(x.a,5);
    12. println!("end");
    13. }

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

    错误代码:

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let mut x:P=P{a:6};
    6. let y= &x;
    7. x.a=5;
    8. if false{
    9. assert_eq!(y.a,5);
    10. }
    11. println!("end");
    12. }

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

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

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let mut x:P=P{a:6};
    6. let y= &x;
    7. let z=&x;
    8. let z2=&z;
    9. let z3=&z2;
    10. let z4=&z3;
    11. assert_eq!(x.a,6);
    12. assert_eq!(y.a,6);
    13. assert_eq!(z.a,6);
    14. assert_eq!(z4.a,6);
    15. assert_eq!((*z4).a,6);
    16. assert_eq!((**z4).a,6);
    17. assert_eq!((***z4).a,6);
    18. assert_eq!((****z4).a,6);
    19. println!("end");
    20. }

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

    3,对变量的可变引用

    正确代码:

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let mut x:P=P{a:6};
    6. let y= &mut x;
    7. assert_eq!(y.a,6);
    8. y.a=5;
    9. assert_eq!(x.a,5);
    10. println!("end");
    11. }

    错误代码:

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let mut x:P=P{a:6};
    6. let y= &mut x;
    7. assert_eq!(x.a,6);
    8. assert_eq!(y.a,6);
    9. println!("end");
    10. }

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

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

    4,函数调用

    错误代码:

    1. fn fun(x:Vec)->i32{
    2. x[0]+1
    3. }
    4. fn main() {
    5. let x = vec![1,2,3];
    6. assert_eq!(fun(x),2);
    7. assert_eq!(x.len(), 3);
    8. println!("end");
    9. }

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

    正确代码:

    1. fn fun(x:&Vec)->i32{
    2. x[0]+1
    3. }
    4. fn main() {
    5. let x = vec![1,2,3];
    6. assert_eq!(fun(&x),2);
    7. assert_eq!(x.len(), 3);
    8. println!("end");
    9. }

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

    5,Borrow、BorrowMut

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

    1. pub trait Borrow {
    2. #[stable(feature = "rust1", since = "1.0.0")]
    3. fn borrow(&self) -> &Borrowed;
    4. }
    5. pub trait BorrowMut: Borrow {
    6. #[stable(feature = "rust1", since = "1.0.0")]
    7. fn borrow_mut(&mut self) -> &mut Borrowed;
    8. }
    9. #[stable(feature = "rust1", since = "1.0.0")]
    10. impl Borrow for T {
    11. #[rustc_diagnostic_item = "noop_method_borrow"]
    12. fn borrow(&self) -> &T {
    13. self
    14. }
    15. }
    16. #[stable(feature = "rust1", since = "1.0.0")]
    17. impl BorrowMut for T {
    18. fn borrow_mut(&mut self) -> &mut T {
    19. self
    20. }
    21. }
    22. #[stable(feature = "rust1", since = "1.0.0")]
    23. impl Borrow for &T {
    24. fn borrow(&self) -> &T {
    25. &**self
    26. }
    27. }
    28. #[stable(feature = "rust1", since = "1.0.0")]
    29. impl Borrow for &mut T {
    30. fn borrow(&self) -> &T {
    31. &**self
    32. }
    33. }
    34. #[stable(feature = "rust1", since = "1.0.0")]
    35. impl BorrowMut for &mut T {
    36. fn borrow_mut(&mut self) -> &mut T {
    37. &mut **self
    38. }
    39. }

    四,引用总结

    1,引用的生命周期

    (1)一个引用变量的生命周期只到它的最后一次读写为止

    (2)如果声明了引用之后没有读写,那么生命周期直接结束,但是这和直接删除这一句不一样,因为声明引用这一行相当于一次读操作

    (3)如果一个引用变量y被z引用了,且z最后一次读写比y的最后一次读写更晚,那么y的生命周期延长到z的最后一次读写。

    PS:如果声明了z是对y的引用之后没有读写,那么声明的这一句就是z的最后一次读操作,这也可能延长y的生命周期。

    讨论引用规则时我们默认只讨论一个生命周期之内的引用

    2,对字面量的引用

    对字面量的引用,无论是可变引用还有不可变引用,其实都不是引用,而是copy拷贝,讨论引用规则时我们默认不把对字面量的引用这个当做引用

    3,对普通变量的引用

    对于普通变量,有mut的是可变变量,没有mut的是不可变变量(常量)。

    可变变量可以加可变引用,也可以加不可变引用,不可变变量只能加不可变引用

    4,对引用变量的引用

    无论是可变引用变量还是不可变引用变量,都和普通变量一样,可能是可变变量也可能是不可能变量

    对引用变量加引用的规则,和对普通变量一致。

    5,对同一变量的引用

    对不可变变量不能加可变引用,可以加多个不可变引用。

    对可变变量可以加唯一的可变引用,也可以加多个不可变引用。

    即,可变引用存在的情况下,只能有一个引用。

    6,链式引用

    以y是对x的引用,z是对y的引用为例,更长的链的情况应该规则类似。

    y的声明周期参考上文“引用的生命周期”。

    在所有情况下,z对x的读写能力都和y对x的读写能力相同,因为z一直持有对y的读能力

    (1)x是不可变变量

    x和y 一直有读能力,没有写能力

    (2)x是可变变量

    在y的最后一次读操作或写操作之前,x没有读写能力,之后,x有读写能力

    y的能力在声明周期内不变,有读能力,有没有写能力取决于是可变引用还是不可变引用。

    五,智能指针

    0,相关通用知识

    (1)deref

    参考deref

    智能指针往往是数据套一个外壳,用deref可以很方便的解引用或者说隐式转换。

    (2)智能指针嵌套

    智能指针嵌套之后,会产生deref隐式转换链,更加的自由多变。

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

    比如borrow_mut函数,既有通用的泛型实现,又有智能指针本身的函数,有时可以通过限制use std::borrow::BorrowMut;生效的范围,来修改默认指向。

    比如RefCell定义了borrow_mut函数,Rc没有,所以Rc>类型的数据既可以调用泛型的borrow_mut函数,也可以通过隐式转换去调用RefCell的borrow_mut函数,默认是前者,因为默认不发生隐式转换。

    即,如果有use std::borrow::BorrowMut;这一句则默认调用泛型的borrow_mut函数,如果没有这一句,则只能调用RefCell的borrow_mut函数。

    1,NonNull

    (1)一句话含义

    *mut T 但是非零且协变。

    (2)源码解析

    1. pub struct NonNull {
    2. pointer: *const T,
    3. }

    2,PhantomData

    (1)一句话含义

    零大小的类型,用来标记像是拥有一个 T 类型的数据。

    (2)源码解析

    pub struct PhantomData;

    3,UnsafeCell

    (1)一句话含义

    内部可变引用。

    (2)具体含义

    普通的&T和&mut T需要满足条件:读和写不能同时存在,写和写不能同时存在。

    如果想突破这个限制,可以使用UnsafeCell

    (3)用法示例

    1. use std::cell::UnsafeCell;
    2. fn main() {
    3. let a : &UnsafeCell= &5.into();
    4. let b=a;
    5. unsafe{
    6. *b.get()=6;
    7. println!("{}",*a.get());
    8. *a.get()=7;
    9. println!("{}",*b.get());
    10. }
    11. println!("end");
    12. }

    输出6 7

    这个例子演示的语义是,a和b持有同一个数据的可变引用。

    (4)安全性

    UnsafeCell转成可变引用是安全的,而解引用是不安全的,所以需要用unsafe包含起来。

    为什么叫UnsafeCell?

    因为共享引用同一个数据,数据是可变的。

    (5)源码解析

    定义:

    1. #[lang = "unsafe_cell"]
    2. #[stable(feature = "rust1", since = "1.0.0")]
    3. #[repr(transparent)]
    4. pub struct UnsafeCell {
    5. value: T,
    6. }

    其中宏lang = "unsafe_cell"告诉编译器对UnsafeCell进行特殊处理,具体的处理内容是不允许协变。

    泛型方法:

    1. pub const fn get(&self) -> *mut T {
    2. self as *const UnsafeCell as *const T as *mut T
    3. }
    4. pub const fn get_mut(&mut self) -> &mut T {
    5. &mut self.value
    6. }

    4,Cell

    (1)一句话含义

    可变的内存位置。

    (2)用法示例

    1. use std::cell::Cell;
    2. fn main() {
    3. let x=Cell::new(1);
    4. let y=x.get();
    5. x.set(2);
    6. assert_eq!(x.get(),2);
    7. assert_eq!(y,1);
    8. println!("end");
    9. }

    这个例子中,y拷贝的是整数值

    1. fn main() {
    2. let x=Cell::new(1);
    3. let y=&x;
    4. x.set(2);
    5. assert_eq!(y.get(),2);
    6. y.set(3);
    7. assert_eq!(x.get(),3);
    8. println!("end");
    9. }

    这个例子演示的语义是,x和y持有同一个数据的可变引用,和上面的UnsafeCell的例子差不多。

    (3)get方法

    1. pub fn get(&self) -> T {
    2. // SAFETY: This can cause data races if called from a separate thread,
    3. // but `Cell` is `!Sync` so this won't happen.
    4. unsafe { *self.value.get() }
    5. }

    执行的是值拷贝,所以get相当于拷贝。

    示例:

    1. fn main() {
    2. let x=std::cell::Cell::new((1,2));
    3. x.get().0 =100;
    4. print!("{:?};",x.get());
    5. }

    输出 (1, 2);

    (4)源码解析

    1. #[stable(feature = "rust1", since = "1.0.0")]
    2. #[repr(transparent)]
    3. pub struct Cell {
    4. value: UnsafeCell,
    5. }

    5,Mutex

    (1)一句话含义

    互斥锁,可用于保护共享数据

    (2)源码解析

    1. pub struct Mutex {
    2. inner: sys::Mutex,
    3. poison: poison::Flag,
    4. data: UnsafeCell,
    5. }

    6,RwLock

    (1)一句话含义

    读写锁

    (2)具体含义

    RwLock最多同时有一个写操作和多个读操作。

    (3)源码解析

    1. pub struct RwLock {
    2. inner: sys::RwLock,
    3. poison: poison::Flag,
    4. data: UnsafeCell,
    5. }

    7,Arc

    (1)一句话含义

    线程安全的引用计数指针

    (2)源码解析

    1. pub struct Arc {
    2. ptr: NonNull>,
    3. phantom: PhantomData>,
    4. alloc: A,
    5. }

    8,RcBox

    9,Rc

    (1)一句话含义

    Rc 是单线程引用计数指针

    (2)用法

    Rc内的数据可以直接用解引用符来用,这是一种通用用法:

    1. use std::rc::Rc;
    2. struct P{
    3. a:i32,
    4. }
    5. fn main() {
    6. let x=Rc::new(P{a:5});
    7. assert_eq!((*x).a,5);
    8. assert_eq!(x.a,5);
    9. println!("end");
    10. }

    部分场景下(如结构体),可以省略解引用符,相当于deref做了一次隐式转换

    引用计数:

    1. fn main() {
    2. let x=Rc::new(1);
    3. let y=Rc::clone(&x);
    4. let y=Rc::clone(&x);
    5. let y=Rc::clone(&x);
    6. println!("{}",Rc::strong_count(&x));
    7. }

    输出4

    每次clone增加1个引用计数,变量析构的时候减少1个引用计数。

    (3)Rc的borrow

    Rc又重新实现了Borrow特征

    1. impl borrow::Borrow for Rc {
    2. fn borrow(&self) -> &T {
    3. &**self
    4. }
    5. }

    这个函数和泛型的borrow是同名的,所以需要这么调用:

    1. let mut x = Rc::new(42);
    2. let y:&Rc = x.borrow();
    3. let z:&i32 = x.borrow();
    4. let y= as Borrow>>::borrow(&x);
    5. let z= as Borrow>::borrow(&x);

    如果简单的写成:

    let y = x.borrow();

    这是编译不过的。

    这种语法和c++是不一样的,c++不能根据返回值类型进行推导重载函数,这里的推导差不多可以理解成根据返回值类型进行推导,当然,也可以理解成一种更复杂的类似于SFINAE的机制,即有且只有一种情况能编译成功的情况下就按照这种情况进行编译。

    PS:

    Rc没有重新实现borrow_mut,所以没有同名冲突。

    因为Rc是引用计数,设计场景用于多个变量持有同一数据的所有权,所以不提供可变引用。

    (4)源码解析

    1. pub struct Rc<
    2. T: ?Sized,
    3. #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
    4. > {
    5. ptr: NonNull>,
    6. phantom: PhantomData>,
    7. alloc: A,
    8. }

    10,Unique

    11,Box

    use std::boxed::Box; 

    1. let five = Box::new(5);
    2. assert_eq!(*five, 5);

    12,RefCell

    (1)一句话含义

    具有动态检查借用规则的可变内存位置

    (2)用法

    1. use std::cell::RefCell;
    2. use std::cell::RefMut;
    3. let x:RefCell=RefCell::new(5);
    4. let y:RefMut = x.borrow_mut();
    5. assert_eq!(*y,5);

    部分场景下(如结构体),可以省略解引用符,相当于deref做了隐式转换:

    1. struct P{
    2. a:i32,
    3. }
    4. fn main() {
    5. let x:RefCell

      =RefCell::new(P{a:5});

    6. let y:RefMut

      = x.borrow_mut();

    7. assert_eq!((*y).a,5);
    8. assert_eq!(y.a,5);
    9. println!("end");
    10. }

    六,智能指针搭配使用

    1,Arc+Mutex

     用法示例

    1. let mutex = Arc::new(Mutex::new(0));
    2. let c_mutex = Arc::clone(&mutex);
    3. thread::spawn(move || {
    4. *c_mutex.lock().unwrap() = 10;
    5. }).join().expect("thread::spawn failed");
    6. assert_eq!(*mutex.lock().unwrap(), 10);

    这个例子中,一个线程中修改了共享值,另外一个线程也会拿到新的值。

    更复杂的例子:

    1. fn main() {
    2. let mutex = Arc::new(Mutex::new(0));
    3. let c_mutex = Arc::clone(&mutex);
    4. thread::spawn(move || {
    5. *c_mutex.lock().unwrap() = 10;
    6. }).join();
    7. match mutex.lock(){
    8. Ok(data)=>{
    9. println!("{}",data);
    10. }
    11. Err(_)=>{
    12. println!("error");
    13. }
    14. };
    15. let mutex = Arc::new(Mutex::new(0));
    16. let c_mutex = Arc::clone(&mutex);
    17. thread::spawn(move || {
    18. let mut data=c_mutex.lock().unwrap();
    19. *data = 10;
    20. }).join();
    21. match mutex.lock(){
    22. Ok(data)=>{
    23. println!("{}",data);
    24. }
    25. Err(_)=>{
    26. println!("error");
    27. }
    28. };
    29. let mutex = Arc::new(Mutex::new(0));
    30. let c_mutex = Arc::clone(&mutex);
    31. thread::spawn(move || {
    32. let mut data=c_mutex.lock().unwrap();
    33. *data = 10;
    34. panic!();
    35. }).join();
    36. match mutex.lock(){
    37. Ok(data)=>{
    38. println!("{}",data);
    39. }
    40. Err(_)=>{
    41. println!("error");
    42. }
    43. };
    44. }

    这个例子中,分别输出10 10 error

    因为panic导致RAII无法正常运行,导致锁没有释放。

    2,Arc+RwLock

    1. fn main() {
    2. let mutex = Arc::new(RwLock::new(0));
    3. let mutex_ref = mutex.clone();
    4. let _=thread::spawn(move||{
    5. let mut data = mutex_ref.write().unwrap();
    6. *data+=1;
    7. }).join();
    8. let data1 = mutex.read().unwrap();
    9. let data2 = mutex.read().unwrap();
    10. print!("{}{}", data1, data2);
    11. }

    输出 1 1

    七,指针总结

    rust一共四类指针:引用、胖指针、智能指针、原始指针

    1,引用

    参考上文

    2,胖指针

    即指向切片的指针,因为既有地址,又有长度所以叫胖指针。(类似c++的数组和指针差不多,但是数组有长度)

    1. struct S{
    2. x:i32
    3. }
    4. fn main() {
    5. let mut c = [1,2,3,4,5,6];
    6. let slice = &mut c[1..3];
    7. slice[0]=9;
    8. assert_eq!(c,[1,9,3,4,5,6]);
    9. }

    这个分类项有争议,因为胖指针可以理解为普通指针,即slice是指向数组[2,3]的普通指针,而数组本身是具有长度的。

    3,智能指针

    参考上一章

    4,原始指针、unsafe块

    可以把一个引用类型,强转成一个原始指针:

    1. fn main() {
    2. let x = 5;
    3. let p = &x as *const i32;
    4. unsafe {
    5. assert_eq!(*p, 5);
    6. }
    7. println!("end");
    8. }

    这里把&x这个&i32类型的数据,转换成了*const i32类型的数据。

    *p是解引用,原始指针解引用必须放在unsafe块中

    1. fn main() {
    2. let mut x = 5;
    3. let p = &mut x as *mut i32;
    4. unsafe {
    5. assert_eq!(*p, 5);
    6. }
    7. println!("end");
    8. }

    &是不可变引用,&mut是可变引用,类似的,*mut 是可变量指针,*const是常量指针。

    转换规则:不可变引用只能转换成常量指针,可变引用可以转换成常量指针或可变量指针

    八,ref

    1. fn main() {
    2. let x=100;
    3. let ref a:&i32=&x;
    4. let b:&&i32=a;
    5. let &a:&i32=&x;
    6. let b:i32=a;
    7. }

    let语句的冒号后面写的类型,要把&和ref和变量本身的类型都带上。

    当我们要引用一个Option的内部成员,可以用ref

    1. struct P{
    2. a:i32,
    3. }
    4. struct Node{
    5. x:Option

      ,

    6. }
    7. fn main() {
    8. let mut p = Node{x:Some(P{a:1})};
    9. if let Some(ref mut x)=p.x{
    10. x.a=2;
    11. }
    12. if let Some(y)=p.x{
    13. assert_eq!(y.a,2);
    14. }
    15. println!("end");
    16. }

    这里的ref mut x得到的x的类型其实就是*mut P类型。

  • 相关阅读:
    股票交易作为事后诸葛,交易次数不同时,返回挣到的最大钱数。
    设计模式-桥接模式
    三十四、Fluent液体喷雾蒸发模拟
    Windows 10 + Jenkins 2.4 安装插件时https 的证书问题及解决
    Neon intrinsics 简明教程
    [Python从零到壹] 五十.图像增强及运算篇之图像直方图理论知识和绘制实现
    运用平台系统流量多久会提升?有什么办法技巧?
    2304. 网格中的最小路径代价 : 从「图论最短路」过渡到「O(1) 空间的原地模拟」
    计算机毕业设计之java+ssm企业销售管理系统
    利用Metasploit控制目标电脑(Win7_x64系统)
  • 原文地址:https://blog.csdn.net/nameofcsdn/article/details/134243013