目录
前言
正文
标量类型
复合类型——元组
复合类型——数组
函数
&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 a
0x000000d6151df964: (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 = 0x00007ff791a1a3b0
length = 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 = 0x000001b36114b6f0
length = 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 = 1
name = "rust" {
data_ptr = 0x00007ff7d53fa3b0
length = 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表示。有点意思