Rust 各类智能指针、所有权和内存区域关系,值得你看!

2024-05-08 23:04

本文主要是介绍Rust 各类智能指针、所有权和内存区域关系,值得你看!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

导航

  • Rust 细究所有权转移发生了什么
  • 一、各类智能指针指向的值是在栈上还是堆上?
    • 1、Box指向的值的地址在哪
    • 2、Arc和Rc指向的值的地址在哪
    • 3、RefCell和Cell指针指向的地址在哪
      • 1)RefCell
      • 2)Cell
    • 我发现所有权转移,同一份数据,是有可能发生数据地址变化的,不只是单纯的拥有者的变化
  • 二、多线程中所有权转移问题?
    • 1、Box
    • 2、Arc
    • 3、RefCell和Cell

Rust 细究所有权转移发生了什么

  • 发这篇博客的原因就是在学习Rust过程中,涉及到很多栈、堆和所有权的问题,导致我开始不清楚一些变量到底是分配在栈上还是堆上,还有从哪到哪,是否发生了内存区域的移动等等
  • 还有下面这种又是什么情况,智能指针指向的值到底在哪?等等一系列问题,emo了~下定决心搞清楚它
let a=1;
let b=Box::new(a);//其实是堆上Copy一个1

一、各类智能指针指向的值是在栈上还是堆上?

1、Box指向的值的地址在哪

fn main() {let mut v1 =1;let b_ptr2 = ptr::addr_of!(v1);println!("栈的地址:{:p}", b_ptr2);let mut v2 =Box::new(String::from("1"));let b_ptr2 = ptr::addr_of!(*v2);println!("String::from在堆中的地址:{:p}", b_ptr2);let  v3 =String::from("1");let b_ptr2 = ptr::addr_of!(v3);println!("v3变量原来在栈中的地址:{:p}", b_ptr2);let t=Box::new(v3);let b_ptr2 = ptr::addr_of!(*t);println!("v3的所有权转移给Box后,v3 的地址:{:p}", b_ptr2);
}

运行一下

栈的地址:0x7ff7b6e931ec
String::from在堆中的地址:0x7fb413f05e20
v3变量原来在栈中的地址:0x7ff7b6e932a0
v3的所有权转移给Box后,v3 的地址:0x7fb413f05e50
  • 可以发现,运行结果的第四行第二行,地址的结果最接近,说明String::from("1")所有权转移Box后,String::from(“1”)转移中了,并且肯定不是Copy,因为String类型没有Copy特征,也肯定不是Clone,因为转移后也无法再次打印v3的地址,你可以试试
  • 结论:Box指向的内容,一定是在堆上的,原来在栈上的也会转移到堆

2、Arc和Rc指向的值的地址在哪

use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("栈的地址:{:p}", b_ptr2);let mut v1 = Rc::new(1);let b_ptr2 = ptr::addr_of!((*v1));println!("v1 变量的地址:{:p}", b_ptr2);let mut v2 = Arc::new(1);let b_ptr2 = ptr::addr_of!((*v2));println!("v2 变量的地址:{:p}", b_ptr2);
}

运行一下

栈的地址:0x7ff7b1cc62a4
v1 变量的地址:0x7f7c31705e20
v2 变量的地址:0x7f7c31705e40
  • 可以发现,v1v2的地址和栈的地址差距很大
  • 结论:RcArc指向的数据,和Box一样,都会分配在堆上

3、RefCell和Cell指针指向的地址在哪

1)RefCell

use std::cell::{Cell, RefCell};
use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("栈的地址:{:p}", b_ptr2);let mut v3 = Box::new(String::from("1"));let b_ptr2 = ptr::addr_of!(*v3);println!("String::from()在被Box拥有时,在堆中的地址:{:p}", b_ptr2);let mut v4 = RefCell::new(*v3); //String::from("1")发生所有权转移let s1 = v4.borrow();let b_ptr2 = ptr::addr_of!((*s1));println!("在RefCell拥有时,String::from() 的地址:{:p}", b_ptr2);// println!("{}",v3);无法打印,可以解开验证一下。
}

我们运行一下

栈的地址:0x7ff7b16dd254
String::from()在被Box拥有时,在堆中的地址:0x7f7ed0f05e20
在RefCell拥有时,String::from() 的地址:0x7ff7b16dd310
  • 可以发现,String::from()Box拥有时,地址是在堆中的,然后转移给RefCell时,数据又移动到栈中了,同一份数据,两次地址完全不同,连区域都变了
  • 结果RefCell指向的值,在栈上

2)Cell

use std::cell::{Cell, RefCell};
use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("栈的地址:{:p}", b_ptr2);let mut v3 = Box::new(1);let b_ptr2 = ptr::addr_of!(*v3);println!("1在被Box拥有时,在堆中的地址:{:p}", b_ptr2);let mut v4 = Cell::new(*v3); //这里没有发生数据转移,直接Copylet s1=v4.get();let b_ptr2 = ptr::addr_of!(s1);println!("在Cell拥有时,1的地址:{:p}", b_ptr2);//println!("{}",v3);//可以打印,可以解开验证一下。
}

运行一下

栈的地址:0x7ff7b628c2ac
1在被Box拥有时,在堆中的地址:0x7ff61bf05e10
在Cell拥有时,1 的地址:0x7ff7b628c34c
  • 可以发现,1Box拥有时,地址是在堆中的,然后转移给Cell时,数据是Copy到栈中了,是两个值两个值的地址完全不同,连区域都变了.因为1是i32类型,i32具备Copy特征。
  • Cell只能用在具有Copy特征的类型上
  • 结果Cell指向的值,在栈上

写到这里

我发现所有权转移,同一份数据,是有可能发生数据地址变化的,不只是单纯的拥有者的变化

二、多线程中所有权转移问题?

1、Box

use std::cell::{Cell, RefCell};
use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("主线程栈的地址:{:p}", b_ptr2);let mut v3 = Box::new(String::from("1"));let b_ptr2 = ptr::addr_of!(*v3);println!("1在主线程的Box拥有时,在堆中的地址:{:p}", b_ptr2);let handle = thread::spawn(move || {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("异步线程内栈的地址:{:p}", b_ptr2);let m = v3; //主线程Box持有的1的所有权转移给了mlet b_ptr2 = ptr::addr_of!(*m);println!("1在异步线程内的Box拥有时,在堆中的地址:{:p}", b_ptr2);});//print!("{}", v3); //不能打印,已经转移给了线程内部handle.join().unwrap();//println!("{}",v3);//可以打印,可以解开验证一下。
}
  • 运行一下
主线程栈的地址:0x7ff7b7a0b2b4
1在主线程的Box拥有时,在堆中的地址:0x7f7de9f05e20
异步线程内栈的地址:0x70000a323bdc
1在异步线程内的Box拥有时,在堆中的地址:0x7f7de9f05e20
  • 可以看到,通过move关键字我们可以将一个值的所有权移动到另一个线程
  • 还可以知道,没有Copy特性数据的所有权转移,同一份数据的地址没有发生改变只是String::from(“1”)的拥有者变了。
  • Copy特性的数据,会复制一份数据,大家可以把String::from(“1”)换成1试试看,篇幅原因我就不展示了

2、Arc

因为是多线程,我们不讨论Rc,因为Rc只能在单线程中正常使用,Arc可以实现在多线程中复制一个值的所有权,但是不能所有权拿走

先说重要结果,Arc拥有的值,不允许在单、多线程中转移所有权,Box可以。看一下代码

use std::cell::{Cell, RefCell};
use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("主线程栈的地址:{:p}", b_ptr2);let mut v3 = Arc::new(String::from("1"));let b_ptr2 = ptr::addr_of!(*v3);println!("1在主线程的Box拥有时,在堆中的地址:{:p}", b_ptr2);let handle = thread::spawn(move || {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("异步线程内栈的地址:{:p}", b_ptr2);let m = *v3; //!!!!!!!编译错误let b_ptr2 = ptr::addr_of!(*m);println!("1在异步线程内的Box拥有时,在堆中的地址:{:p}", b_ptr2);});handle.join().unwrap();}

3、RefCell和Cell

这个是控制可变性和绕过编译器借用规则,并且RefCell的borrow和borrow_mut 不会抢走所有权。我们来看一下

use std::cell::{Cell, Ref, RefCell};
use std::ptr;
use std::rc::Rc;
use std::sync::*;
use std::sync::{Condvar, Mutex};
use std::thread;fn main() {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("主线程栈的地址:{:p}", b_ptr2);let v3 = RefCell::new(String::from("1"));let b_ptr2 = ptr::addr_of!(v3);println!("RefCell的地址:{:p}", b_ptr2);let handle = thread::spawn(move || {let mut v1 = 1;let b_ptr2 = ptr::addr_of!(v1);println!("RefCel在异步线程内栈的地址:{:p}", b_ptr2);let m = v3; //编译错误let b_ptr2 = ptr::addr_of!(m);println!("RefCell的地址:{:p}", b_ptr2);});handle.join().unwrap();
}

运行一下

主线程栈的地址:0x7ff7b5993294
RefCell::new(String::from()的地址:0x7ff7b59932e0
RefCell::new(String::from()在异步线程内栈的地址:0x7000066ddad4
RefCell::new(String::from()的地址:0x7000066ddb20
  • 我没有在主函数中,调用v3的.borrow,因为借用后不能发生所有权转移
  • 其次,我暂时没有办法不通过borrow的方式访问RefCell指向的值的地址,如果用inner_into,也会发生所有权转移。头大~
  • 所以上面只能简单验证了RefCell作为一个变量在线程之间的所有权转移,String::from(“1”)换了一个拥有者
    欢迎大家关注我的博客
    在这里插入图片描述

这篇关于Rust 各类智能指针、所有权和内存区域关系,值得你看!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/971685

相关文章

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

Rust 数据类型详解

《Rust数据类型详解》本文介绍了Rust编程语言中的标量类型和复合类型,标量类型包括整数、浮点数、布尔和字符,而复合类型则包括元组和数组,标量类型用于表示单个值,具有不同的表示和范围,本文介绍的非... 目录一、标量类型(Scalar Types)1. 整数类型(Integer Types)1.1 整数字

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

菲律宾诈骗,请各位华人朋友警惕各类诈骗。

骗子招聘类型:程序开发、客服、财务、销售总管、打字员等 如果有人用高薪、好的工作环境来你出国工作。要小心注意!因为这些骗子是成群结伴的! 只要你进入一个菲律宾的群,不管什么类型的群都有这些骗子团伙。基本上是他们控制的! 天天在群里有工作的信息,工作信息都是非常诱惑人的。例如招“打字员”、“客服”、“程序员”……各种信息都有。只要你提交简历了,他会根据你的简历判断你这个人如何。所谓的心理战嘛!

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

智能交通(二)——Spinger特刊推荐

特刊征稿 01  期刊名称: Autonomous Intelligent Systems  特刊名称: Understanding the Policy Shift  with the Digital Twins in Smart  Transportation and Mobility 截止时间: 开放提交:2024年1月20日 提交截止日