使用lldb查看Rust不同类型的结构

目录

前言

正文

标量类型

复合类型——元组

 复合类型——数组

函数

&str

struct

可变数组vec

Iter

String

Box

Rc

Arc

 RefCell

Mutex

RwLock

Channel

总结


前言

笔者发现这个lldb挺好玩的,可以查看不同类型的结构,虽然这好像是C++的东西,

看看Rust的也可以,

笔者使用RustRover中的调试模式,在后面的代码,在打印语句哪一行打断点。

输出使用

expr    <变量>

正文

参考如下

数据类型 - Rust 程序设计语言 中文版Rust 程序设计语言中文也译为 Rust 权威指南,是 Rust 官方推出的学习 Rust 的必备教程。Rust Wiki 版的 Rust 程序设计语言简体中文版将由 Rust 中文翻译项目组持续维护和更新,确保内容最新最全。https://www.rustwiki.org.cn/zh-CN/book/ch03-02-data-types.html

标量类型

标量scalar)类型表示单个值。Rust 有 4 个基本的标量类型:整型、浮点型、布尔型和字符

比如

    let a=1;println!("{a}")

输出

(lldb) expr a
(i32) a = 1

i32表示是32为的整数,当前值为1

获取其地址

(lldb) expr &a
(*mut i32) &a = 0x000000839274f5f4

加个引用符号。

对于其他类似的,比如u32,u8、true等,这些标量类型都是放在某一个地址中,没有其他东西。

使用其他命令——frame variable -L

(lldb) frame variable -L a0x000000d6151df964: (i32) a = 1

可以看到输出了地址、类型、变量、值。

在tauri中没有什么区别。

复合类型——元组

看看元组内部长什么样的

比如

    let a=(1,2.0,'g');println!("{a:?}")

输出

(lldb) expr a
(tuple$<i32,f64,char>) a = (__0 = 1, __1 = 2, __2 = 'g')

可以使用 .0或者__0访问其中的数据

(lldb) expr a.__0
(i32) __0 = 1
(lldb) expr a.0
(i32) __0 = 1

看来这个元组是多个标量和在一起的类型

在rust代码中,无法使用 __0

类型 `(i32, f64, char)` 中没有字段 `__0` [E0609]

 在tauri中没有什么区别。

 复合类型——数组

    let a=[1,2,3];println!("{a:?}")

输出

(lldb) expr a
([i32; 3]) a = ([0] = 1, [1] = 2, [2] = 3)

这里是i32类型,长度为3的数组。

在lldb访问其中的值,可以使用a[0],也可以使用a.0

(lldb) expr a[0]
(i32) [0] = 1
(lldb) expr a.0
(i32) [0] = 1

函数

如果是一个函数

fn f1()->i32{return 1; 
}
fn main() {let a=f1;println!("123");}

a会是什么? 输出看看

(lldb) expr a
(*mut ) a = 0x0000000011117b00

*mut 表示是一个可变的指针

笔者想调用a,但是没成功

(lldb) expr a()
error: function call failed

不知道什么情况。

&str

    let x="abc";println!("{x}");

换个字母。

看看&str的在lldb的输出

(lldb) expr x
(ref$<str$>) x = "abc" {data_ptr = 0x00007ff791a1a3b0length = 3
}

有两个东西,笔者都不知道改怎么称呼,就叫 字段,

有两个字段,一个data_ptr,显而易见,是存放数据的地址,可以修改

另一个length,就是长度了。

进去地址看看

(lldb) expr x.data_ptr
(*mut u8) data_ptr = 0x00007ff791a1a3b0

 发现又是是个可变指针,指向一个u8类型的内存地址

可以加个*访问了

(lldb) expr *(x.data_ptr)
(u8) *data_ptr = 97

发现是97,这不就是a的ASCLL码,

访问一下其他的

(lldb) expr *x.data_ptr+1
(u32)  = 98
(lldb) expr *x.data_ptr+2
(u32)  = 99

没问题。

笔者本来想修改数据的,没想到失败了

(lldb) memory region x.data_ptr
[0x00007ff688860000-0x00007ff688883000) r--

memory region 是 LLDB 调试器中的一个命令,

用于显示指定内存地址所在的内存区域的属性和权限信息。

它会告诉你某个地址是否可读、可写、可执行,以及该内存区域的起始和结束范围。

可以发现,只有r,只有只读

笔者就算在变量设置mut,内存还是还是只读

但是,可以修改长度

(lldb) expr (x).length=1
(u64) length = 1

总之,这个&str就像一个“结构体”,感觉不是很准确,应该说像“json”。

笔者发现tauri 通信函数greet

#[command]
fn greet(name: &str) -> String {println!("Hello, {}!", name);format!("Hello, {}! You've been greeted from Rust!", name)
}

这个name,内存居然拥有写的权限

(ref$<str$>) name = "world" {data_ptr = 0x000001b36114b6f0length = 5
}
(lldb) memory region 0x000001b36114b6f0
[0x000001b361050000-0x000001b36114f000) rw-

笔者不能理解 

struct

看看结构体

如下,

    struct book<'a>{id: i32,name:&'a str,}let book1 = book{id: 1,name: "rust",};println!("{}", book1.name);

输出

(lldb) expr book1
(shared_state_concurrency::main::book) book1 = {id = 1name = "rust" {data_ptr = 0x00007ff7d53fa3b0length = 4}
}

真像json,比如取值——length

(lldb) expr book1.name.length
(u64) length = 4

没问题

可变数组vec

    let a=vec![1, 2, 3];println!("{:?}", a);

输出

(lldb) expr a
(alloc::vec::Vec<i32,alloc::alloc::Global>) a = size=3 {[0] = 1[1] = 2[2] = 3
}

获取第一个字段——buf,a.0、a[0]、或者a.buf,都行

(lldb) expr a.buf
(alloc::raw_vec::RawVec<i32,alloc::alloc::Global>) buf = {inner = {ptr = {pointer = {pointer = 0x000002b284ccfc20}_marker = {}}cap = (__0 = 3)alloc = {}}_marker = {}
}

看看这个地址可不可以写

(lldb) memory region 0x000002b284ccfc20
[0x000002b284cb0000-0x000002b284cd0000) rw-

 发现有w,可以写,改成66 77 88。

修改数据

memory write -s 4 0x000001d9e06a7430 42 4d 58

因为是i32类型的,32位,需要4个字节,

66是十进制,变成16进制是42

其他同理。写完后

(lldb) expr a(alloc::vec::Vec<i32,alloc::alloc::Global>) a = size=3 {[0] = 66[1] = 77[2] = 88
}

没问题

Iter

看看迭代器

    let a=vec![1,2,3];let iter= a.iter();println!("{a:?}")

如果以json数据表示iter的结构,如下

{"iter": {"ptr": {"pointer": "0x000001dd45d299f0"},"end_or_len": "0x000001dd45d299fc","_marker": {}}
}

发现这个iter,pointer和 end_or_len都是地址,

意思就很明显了,从pointer开始,到end_or_len结束。

f0到fc ,中间有12个字节,类型是i32的,没问题。

String

    let a=String::from("hello");println!("{:?}", a);

输出

(lldb) expr a
(alloc::string::String) a = "hello" {vec = size=5 {[0] = 104[1] = 101[2] = 108[3] = 108[4] = 111}
}

可以看到String里面放了一个vec,

(lldb) expr a.vec
(alloc::vec::Vec<u8,alloc::alloc::Global>) vec = size=5 {[0] = 104[1] = 101[2] = 108[3] = 108[4] = 111
}

这个vec元素的类型还是u8。

如果考虑成json结构,可能是这样的

{"a":{"vec": {"buf": {"inner": {"ptr": {"pointer": {"pointer": "0x000001651effdeb0"},"_marker": {}},"cap": {"__0": 5},"alloc": {}},"_marker": {}},"len": 5}}
}

没问题

Box

Box是智能指针,允许将一个值放在堆上而不是栈上

    let a=Box::new(1);println!("{:?}", a);

输出,看看a长什么样

(lldb) expr a
(*mut i32) a = 0x00000279d903de90

确实是一个指针

获取其中的值*a

(lldb) memory region 0x00000279d903de90
error: 'jb_renderers_set_markup' is not a valid command.
[0x00000279d9030000-0x00000279d9050000) rw-

有写的权限 

Rc

Rc被称为 引用计数

    let a=Rc::new(1);println!("{:?}", a);

输出 

(lldb) expr  a
(alloc::rc::Rc<i32,alloc::alloc::Global>) a = strong=1, weak=0 {value = 1
}

这个Rc就比Box要复杂的得多

使用json表示内部的结构

{"a:rc":{"ptr": {"pointer":{"strong":{"value": {"value": 1}},"weak":{"value": {"value": 1}},"value":1}},"phantom":{},"alloc": {}}
}

表示的不是很准确,因为pointer其实是一个指针。

(lldb) expr a.ptr.pointer
(*mut alloc::rc::RcInner<i32>) pointer = 0x000001eb490a7710

拥有写的权限


(lldb) memory region 0x000001eb490a7710
[0x000001eb49090000-0x000001eb490b0000) rw-

使用一次clone,

let b=Rc::clone(&a);

发现strong变成了2

(lldb) expr a.ptr.pointer.strong.value.value
(u64) value = 2

Arc

Arc原子引用计数指针,可以安全地在多线程环境中共享数据

结构和Rc几乎一模一样,但是其中的类型不一样。笔者就不展示了

 RefCell

允许你即使在有不可变引用时也可以改变数据

    let a=RefCell::new(1);println!("{:?}", a);

输出用json表示

  "a:RefCell":{"value": {"value": 1},"borrowed": {"value": {"value": 0}}}

这个RefCell 有点高级,笔者没有看到关于地址的东西

使用一下

    {let mut b=a.borrow_mut();*b=2;println!("{:?}", a);}

在大括号里面,发现这个a的borrow的值

(lldb) expr a.borrow.value.value
(i64) value = -1

居然变成了-1,有点意思

Mutex

看看互斥锁

    let a=Mutex::new(1);println!("{a:?}")

输出,用json表示结构

{"a:Mutex":{"inner": {"futex": {"v": {"value": 0}}},"poison": {"failed": {"v": {"value": 0}}},"data": {"value": 1}}
}

如果使用了lock

let lock=a.lock().unwrap();

可以发现futex的值变成了1

(lldb) expr a.inner.futex.v.value
(u8) value = 1

RwLock

    let a=RwLock::new(1);println!("{a:?}")

其结构用json表示

{"a:RwLock": {"inner": {"state": {"v": {"value": 0}},"writer_notify": {"v": {"value": 0}}},"poison": {"failed": {"v": {"value": 0}}},"data": {"value": 1}}
}

和Mutex差不多,但是inner内部变了

很容易猜测,使用一次读锁,state对应的值变成1

使用一次写锁writer_notify对应的值变成1

但是,笔者使用读锁,确实如下

 let b=a.read().unwrap();
(lldb) expr a.inner.state.v.value
(u32) value = 1

使用写锁

let mut b=a.write().unwrap();

发现并不是writer_notify变成1

(lldb) expr a
(std::sync::poison::rwlock::RwLock<i32>) a = {inner = {state = {v = (value = 1073741823)}writer_notify = {v = (value = 0)}}poison = {failed = {v = (value = 0)}}data = (value = 1)
}

而是这个state变成了一个很大的值,1073741823,这个值感觉不是巧合,笔者不能理解。

写锁是独占的。

笔者添加4个读锁,发现state对应的值变成了4

看来根据这个state的值可以判断是读锁还是写锁。具体实现笔者不是很清楚,必然和state有很大的关系。

Channel

看看通道

use std::sync::mpsc::channel;
use std::thread;
fn main() {let (tx, rx) = channel();thread::spawn(move || {let val = String::from("hi");tx.send(val).unwrap();});println!("123")
}

tx 和rx用json表示

{"tx": {"inner": {"flavor": {"0": {"counter": "0x0000020b23389b00"}}}},"rx": {"inner": {"flavor": {"0": {"counter": "0x0000020b23389b00"}}}}
}

可以发现,二者的结构是一模一样的。最后都指向一个地址。

 意思就显而易见了,把某个消息传递到某个地址,然后再从这个地址中获取消息

这就是通道吗?有点意思。

总结

看了看,rust的不同类型的结构

感觉这个结构,无论是什么,好像都可以用json表示。有点意思

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp使用ui.request 请求流式输出

正文&#xff1a; 在现代Web开发中&#xff0c;实时数据流和长时间运行的请求变得越来越常见&#xff0c;尤其是在处理大量数据或进行实时通信时。在这种情况下&#xff0c;uniapp 提供的 ui.request 请求方法可以帮助我们轻松实现流式输出请求。本文将介绍如何使用 uni.reques…

如何恢复被勒索软件加密的服务器文件(解密与备份策略)

针对勒索软件加密文件的恢复和解密策略&#xff0c;结合当前数据安全最佳实践&#xff0c;整理应对指南如下&#xff1a; 一、文件解密与修复方法 立即隔离设备‌ 断开网络连接并禁用共享功能&#xff0c;防止病毒横向传播 通过文件后缀异常&#xff08;如.locked、.wxx&…

JS,ES,TS三者什么区别

Java Script(JS)、ECMAScript(ES)、TypeScript(TS) 的核心区别与关联的详细解析,结合技术背景、设计目标及应用场景展开说明: 一、核心定义与关系 JavaScript(JS) 定义:一种动态类型、基于原型的脚本语言,由 Netscape 公司于 1995 年首次开发,用于网页交互功能。角…

【MapReduce入门】深度解析MapReduce:定义、核心特点、优缺点及适用场景

目录 1 什么是MapReduce&#xff1f; 2 MapReduce的核心特点 2.1 分布式处理 2.2 容错机制 3 MapReduce的完整工作流程 4 MapReduce的优缺点分析 4.1 优势 4.2 局限性 5 MapReduce典型应用场景 5.1 适用场景 5.2 不适用场景 6 MapReduce与其他技术的对比 7 总结 1…

【Redis】分布式锁的实现

目录 一、本地锁存在的问题 二、redis实现分布式锁原理 三、使用示例 四、锁误删问题 解决思路 获取锁和释放锁代码优化 五、锁释放的原子性问题 解决思路&#xff08;Lua脚本&#xff09; 使用流程 总结 大家好&#xff0c;我是千语。上期给大家讲了使用悲观锁来解决…

Unity3D对象池设计与实现详解

前言 在Unity3D中&#xff0c;对象池&#xff08;Object Pooling&#xff09;是一种优化技术&#xff0c;用于减少频繁实例化和销毁对象带来的性能开销。以下是对象池的详细设计和实现步骤&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以点…

[Spring]-组件的生命周期

组件生命周期 认识组件的声明周期 实验1 通过Bean指定组件的生命周期 package com.guigu.spring.ioc.bean;Data public class User {private String username;private String password;private Car car;Autowiredpublic void setCar(Car car) {System.out.println("自动…

【golang】网络数据包捕获库 gopacket

详解 github.com/google/gopacket/pcap 包 github.com/google/gopacket/pcap 是 Go 语言中一个强大的网络数据包捕获库&#xff0c;它是 gopacket 项目的一部分&#xff0c;提供了对 libpcap&#xff08;Linux/Unix&#xff09;和 WinPcap&#xff08;Windows&#xff09;的 G…

RBTree的模拟实现

1&#xff1a;红黑树的概念 红⿊树是⼀棵⼆叉搜索树&#xff0c;他的每个结点增加⼀个存储位来表⽰结点的颜⾊&#xff0c;可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束&#xff0c;红⿊树确保没有⼀条路径会⽐其他路径⻓出2倍&#xff0c;因…

React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例

React Router 中的 unstable_usePrompt 是一个用于在用户尝试离开当前页面时触发确认提示的自定义钩子&#xff0c;常用于防止用户误操作导致数据丢失&#xff08;例如未保存的表单&#xff09;。 一、unstable_usePrompt用途 防止意外离开页面&#xff1a;当用户在当前页面有…

OSI 7层模型

OSI 7层模型&#xff1a; 1、物理层&#xff08;光纤等把电脑连接起来的物理手段&#xff09; 2、数据链路层&#xff08;以太网&#xff0c;确认0和1电信号的分组方式&#xff0c;负责MAC地址&#xff0c;MAC地址用于在网络中唯一标示一个网卡&#xff0c;相当于网卡的身份证…

视频编解码学习十一之视频原始数据

一、视频未编码前的原始数据是怎样的&#xff1f; 视频在未编码前的原始数据被称为 原始视频数据&#xff08;Raw Video Data&#xff09;&#xff0c;主要是按照帧&#xff08;Frame&#xff09;来组织的图像序列。每一帧本质上就是一张图片&#xff0c;通常采用某种颜色格式…

Redis学习打卡-Day1-SpringDataRedis、有状态无状态

Redis的Java客户端 Jedis 以 Redis 命令作为方法名称&#xff0c;学习成本低&#xff0c;简单实用。Jedis 是线程不安全的&#xff0c;并且频繁的创建和销毁连接会有性能损耗&#xff0c;因此推荐使用 Jedis 连接池代替Jedis的直连方式。 lettuce Lettuce是基于Netty实现的&am…

告别静态配置!Spring Boo动态线程池实战指南:Nacos+Prometheus全链路监控

一、引言 1.1 动态线程池的必要性 传统线程池的参数&#xff08;如核心线程数、队列容量&#xff09;通常通过配置文件静态定义&#xff0c;无法根据业务负载动态调整。例如&#xff0c;在电商大促场景中&#xff0c;流量可能瞬间激增&#xff0c;静态线程池容易因配置不合理导…

Flask如何读取配置信息

目录 一、使用 app.config 读取配置 二、设置配置的几种方式 1. 直接设置 2. 从 Python 文件加载 3. 从环境变量加载 4. 从字典加载 5. 从 .env 文件加载&#xff08;推荐开发环境用&#xff09; 三、读取配置值 四、最佳实践建议 在 Flask 中读取配置信息有几种常见方…

【React中useCallback钩子详解】

useCallback 是 React 中的一个性能优化 Hook,用于缓存函数引用,避免在组件重新渲染时重复创建相同的函数,从而减少不必要的子组件渲染或副作用执行。以下是其核心要点: 1. 核心作用 函数记忆化:返回一个记忆化的回调函数,仅在依赖项变化时重新创建函数,否则复用之前的函…

【!!!!终极 Java 中间件实战课:从 0 到 1 构建亿级流量电商系统全链路解决方案!!!!保姆级教程---超细】

终极 Java 中间件实战课:电商系统架构实战教程 电商系统架构实战教程1. 系统架构设计1.1 系统模块划分1.2 技术选型2. 环境搭建2.1 开发环境准备2.2 基础设施部署3. 用户服务开发3.1 创建Maven项目3.2 创建用户服务模块3.3 配置文件3.4 实体类与数据库设计3.5 DAO层实现3.6 Se…

C#异步Task,await,async和Unity同步协程

标题 TaskawaitasyncUnity协程 Task Task是声明异步任务的必要关键字&#xff0c;也可以使用Task<>泛型来定义Task的返回值。 await await是用于等待一个Task结束&#xff0c;否则让出该线程控制权&#xff0c;让步给其他线程&#xff0c;直到该Task结束才往下运行。 …

【USRP】在linux下安装python API调用

UHD 源码安装 安装库 sudo apt-get install autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool \ g git inetutils-tools libboost-all-dev libncurses5 libncurses5-dev libusb-1.0-0 libusb-1.0-0-dev \ libusb-dev python3-dev python3-mako …

什么是 NoSQL 数据库?它与关系型数据库 (RDBMS) 的主要区别是什么?

我们来详细分析一下 NoSQL 数据库与关系型数据库 (RDBMS) 的主要区别。 什么是 NoSQL 数据库&#xff1f; NoSQL (通常指 “Not Only SQL” 而不仅仅是 “No SQL”) 是一类数据库管理系统的总称。它们的设计目标是解决传统关系型数据库 (RDBMS) 在某些场景下的局限性&#xf…