首页 > 系统相关 >rust学习十五.5、引用循环和内存泄露

rust学习十五.5、引用循环和内存泄露

时间:2025-01-07 20:45:11浏览次数:10  
标签:count leaf 内存 Rc println new strong 泄露 rust

这个章节主要是为了引出弱引用这个奇怪的概念。

说实在,这个没有什么太好写的,因为内容比较晦涩难懂!

 

在其它语言中,也常常存在所谓循环引用问题,和大部分问题一样,在其它语言中这些基本上都不是问题。但是在rust中,这是一个致命的问题。

 

例如有a,b两个点,a指向b,b指向a。

如果根据截止当前的知识(所有权、一个数据也可以有多个所有者),那么rust可能无法释放a所实际指向的数据,或者是因为循环递归导致堆栈溢出。

或者其实我们也没有必要知道为什么,只要知道结论即可,毕竟如果我们不看编译器是怎么写的。所以,有的时候就是看到了概念,看到了例子也是难以理解。

 

这些都需要借助于一些概念和例子来理解。

堆栈溢出示例(来自原书):

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
enum List {
    Cons(i32, RefCell<Rc<List>>),
    Nil,
}

impl List {
    fn tail(&self) -> Option<&RefCell<Rc<List>>> {
        match self {
            Cons(_, item) => Some(item),
            Nil => None,
        }
    }
}

fn main() {
    let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));

    println!("a initial rc count = {}", Rc::strong_count(&a));
    println!("a next item = {:?}", a.tail());

    let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));

    println!("a rc count after b creation = {}", Rc::strong_count(&a));
    println!("b initial rc count = {}", Rc::strong_count(&b));
    println!("b next item = {:?}", b.tail());

    if let Some(link) = a.tail() {
        *link.borrow_mut() = Rc::clone(&b);
    }

    println!("b rc count after changing a = {}", Rc::strong_count(&b));
    println!("a rc count after changing a = {}", Rc::strong_count(&a));

    // Uncomment the next line to see that we have a cycle;
    // it will overflow the stack
    // println!("a next item = {:?}", a.tail());
}

 

rust在做匹配的时候,会不断找尾巴,结果循着尾巴一直找.....

 

rust推出一个新的概念:弱引用(weak reference)

所谓弱引用,即指向某个数据,但是这种引用不会产生所有权方面的问题,就如同大部分其它语言那样的美好。

 

一、重要概念

Rc::downgrade Rc::downgrade 会将 weak_count 加 1,同时返回Weak<T>指针 这个函数用于构建弱引用   Weak::upgrade() Weak<T> 实例的 upgrade 方法,这会返回 Option<Rc<T>>  

二、例子

书上例子很完善,稍微修改了下:

use std::cell::RefCell;
use std::rc::{ Rc, Weak };

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

fn main() {
    look_weak_ref();
}

fn look_weak_ref() {
    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
    {
        println!("------------------------------------------------------------------------------");

        let branch = Rc::new(Node {
            value: 5,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![Rc::clone(&leaf)]),
        });
        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);
        println!("branch strong = {}, weak = {}",Rc::strong_count(&branch),Rc::weak_count(&branch));
        println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
        println!("------------------------------------------------------------------------------");
    }

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
    println!("leaf strong = {}, weak = {}", Rc::strong_count(&leaf), Rc::weak_count(&leaf));
}

 

三、小结

如果我们成心构建导致堆栈溢出的死循环,还是可以的,书上也有这个例子。

但rust提供了可以避免这个的方法,毕竟这种需求还是不鲜见的!

 

标签:count,leaf,内存,Rc,println,new,strong,泄露,rust
From: https://www.cnblogs.com/lzfhope/p/18655762

相关文章

  • 场景题:假设有40亿QQ号,但只有1G内存,如何实现去重?
    当数据量比较大时,使用常规的方式来判重就不行了。例如,使用MySQL数据库判重,或使用List.contains()或Set.contains()判重就不行了,因为数据量太大会导致内存放不下,或查询速度太慢等问题。1.空间占用量预测正常情况下,如果将40亿QQ号存储在Java中的int类型的话,一个int......
  • AppDomainManager注入是一种针对.NET应用程序的高级攻击技术,攻击者通过操控AppDomain
    什么是APPDomainManager注入?APPDomainManager注入通常涉及到利用**应用程序域(AppDomain)**来执行恶意操作,特别是在.NET环境下。要理解这个概念,我们需要了解几个关键术语:AppDomain:在.NET应用程序中,AppDomain是一个隔离的执行环境,它允许多个应用程序或应用程序的不同部分在同一进......
  • 1. 简述一下JVM内存模型
    JVM内存分为线程私有区和线程共享区线程私有区:程序计算器当同时进行的线程数超过CPU数或内核数时,就要通过时间片轮徇分派CPU的时间资源,不免发生线程切换。这时,每个线程就需要一个属于自己的计数器来记录下一条要运行的指令。如果执行的是JAVA方法,计数器记录正在执行的java字......
  • 【Rust自学】11.1. 编写和运行测试
    喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)11.1.1.什么是测试在Rust里一个测试就是一个函数,它被用于验证非测试代码的功能是否和预期一致。在一个测试的函数体里通常执行3个操作:准备(Arrange)数据/状态运......
  • 【Rust自学】11.2. 断言(Assert)
    喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)11.2.1.使用assert!宏检查测试结果assert宏来自标准库,用来确定某个状态是否为true。这个宏可以接收一个返回类型为布尔类型的表达式:当assert!内的值为true时测试......
  • 核弹级0day深信服运维安全管理系统(堡垒机)存在信息泄露漏洞
     0x01产品概述        深信服运维安全管理系统侧重于运维安全管理,集账号管理、身份认证、单点登录、资源授权、访问控制和操作审计为一体,能够对IT资产(如服务器、网络设备、安全设备、数据库等)的操作过程进行有效的运维操作审计,使运维审计由事件审计提升为操作内容......
  • Rust在前端领域有哪些应用?
    Rust在前端领域的应用正逐渐扩展,并以其高性能和安全性受到开发者的关注。以下是一些Rust在前端开发的典型应用:高性能的WebAssembly:Rust可以编译成WebAssembly,这是一种现代Web浏览器支持的二进制代码格式。通过WebAssembly,Rust编写的程序能在浏览器中作为原生代码运行,显著提升......
  • 说说你对Rust的了解?
    关于Rust在前端开发中的应用和了解,我可以从以下几个方面进行阐述:一、Rust语言特性Rust是一种系统编程语言,它提供了内存安全、并发性和高性能等关键特性,这些特性使得Rust在前端开发中也有其独特的应用价值。内存安全:Rust通过所有权系统和借用检查器在编译时捕获许多常见的内存......
  • 你觉得前端开发人员有必要学习Rust吗?
    对于前端开发人员是否有必要学习Rust,这个问题可以从多个角度进行分析。首先,从性能优势的角度来看,Rust确实具有吸引力。作为一种编译型语言,Rust能编译成高效的机器码,提供接近于C/C++的性能。在处理复杂的数据计算或图像处理等需要高性能处理的前端项目中,Rust可以发挥重要作用。此......
  • 你有使用过Rust写过什么应用吗?
    是的,我使用过Rust进行前端开发,具体来说,我主要利用Rust在以下几个方面的优势来构建前端应用:高性能的WebAssembly编译:Rust可以编译成WebAssembly,这是一种在现代Web浏览器中运行的二进制代码格式。通过这种方式,Rust编写的程序能够在浏览器中以原生代码的形式运行,从而极大地提高了......