引用、类型与原始指针解释
学习内容
- 了解和学习Rust语言引用
Reference
、类型与原始指针Pointer
关系
篇目
引用类型&str
与原始指针关系图
# #![allow(unused_variables)] #fn main() { // File: lib-hello/src/immut/raw_pointer/mod.rs // Function: use_raw_pointer_str() use std::slice; println!(); let instance: String = String::from("Hello"); let ref_raw: *const u8 = instance.as_ptr(); println!("instance value = {}", instance); println!("instance reference raw address = {:?}", ref_raw); println!(); let ref_str: &str = &instance; let ref_str = instance.as_str(); let ref_str: &str = instance.as_str(); let ref_str: &str = &instance[0..=4]; println!("ref_str value = {}", ref_str); println!("ref_str owned address = {:p}", ref_str); let ref_raw_str: *const u8 = ref_str.as_ptr(); assert_eq!(ref_raw, ref_raw_str); println!(); let ref_slice: &[*const u8] = unsafe { slice::from_raw_parts(&ref_raw, 5) }; dbg!(ref_slice); assert_eq!(&ref_raw, &ref_slice[0]); println!(); let u8_slice: &[u8] = unsafe { slice::from_raw_parts(ref_raw, 5) }; dbg!(u8_slice); #}
第一段代码:绑定字符串String
类型的变量instance
println!();
let instance: String = String::from("Hello");
let ref_raw: *const u8 = instance.as_ptr();
println!("instance value = {}", instance);
println!("instance reference raw address = {:?}", ref_raw);
在这一段代码里,第一个let
绑定了字符串String
类型变量instance
。这样就产生了上面图的变量instance
及其原始指针。
字符串String
类型由三个部分组成:指向原始指针的指针地址、其长度和容量。该指针地址指向内部缓冲字符串(buffer string),用于存储其数据。
在这一段代码里,第二个let
绑定了*const u8
类型变量ref_raw
。其中方法as_ptr()
功能是将字符串切片转换为原始指针。
第二段代码:绑定字符串文字&str
类型的变量instance
println!();
let ref_str: &str = &instance;
let ref_str = instance.as_str();
let ref_str: &str = instance.as_str();
let ref_str: &str = &instance[0..=4];
println!("ref_str value = {}", ref_str);
println!("ref_str owned address = {:p}", ref_str);
let ref_raw_str: *const u8 = ref_str.as_ptr();
在这一段代码里,前面四个let
绑定了字符串文字&str
类型变量ref_str
,它们是完全等效的。其中方法as_str()
功能是提取包含整个String
的字符串切片。
在这一段代码里,最后的let
绑定了*const u8
类型变量ref_raw_str
。特别需要注意的是,变量ref_raw_str
也是原始指针的地址。
第三段代码:第一次验证原始指针地址
这里将验证,第一段代码和第二段代码的原始指针变量ref_raw
和ref_raw_str
的地址是相等的。
assert_eq!(ref_raw, ref_raw_str);
第四段代码:原始指针内存地址的数组切片
在这一段代码里,使用了关键词unsafe
,然后启动一个包含不安全代码的新代码块。该代码块存在方法from_raw_parts(),它是根据原始指针的引用及其长度返回一个内存地址数组切片。这数组切片的每一项是字符串其中一个字符的内存地址。
println!();
let ref_slice: &[*const u8] = unsafe { slice::from_raw_parts(&ref_raw, 5) };
dbg!(ref_slice);
第五段代码::第二次验证原始指针地址
这里将验证,两个原始指针,第一段代码的变量ref_raw
和第四段代码数组切片ref_slice
的第一项的地址是相等的。
assert_eq!(&ref_raw, &ref_slice[0]);
第六段代码:原始指针字符值的数组切片
在这一段代码里,使用了关键词unsafe
,然后启动一个包含不安全代码的新代码块。该代码块存在方法from_raw_parts(),它是根据原始指针及其长度返回一个类型u8数组切片。这类型u8数组切片的每一项是字符串其中的一个字符值。
println!();
let u8_slice: &[u8] = unsafe { slice::from_raw_parts(ref_raw, 5) };
dbg!(u8_slice);
程序输出结果
该程序输出结果如下,可以比较上面的阐述:
[bin-hello/examples/use_raw_pointer_str.rs:35] ref_slice = [
0x00007fa772403730,
0x00000001034db528,
0x0000000000000002,
0x0000000000000000,
0x00007fff5c750d80,
]
[bin-hello/examples/use_raw_pointer_str.rs:45] u8_slice = [
72,
101,
108,
108,
111,
]
───────┬─────────────────────────────────────────────────────────────────────────
│ STDIN
───────┼─────────────────────────────────────────────────────────────────────────
1 │
2 │ instance value = Hello
3 │ instance reference raw address = 0x7fa772403730
4 │
5 │ ref_str value = Hello
6 │ ref_str owned address = 0x7fa772403730
7 │
8 │
───────┴─────────────────────────────────────────────────────────────────────────
引用类型&String
与原始指针关系图
# #![allow(unused_variables)] #fn main() { // File: lib-hello/src/immut/raw_pointer/mod.rs // Function: use_raw_pointer_string() use std::slice; println!(); let instance: String = String::from("Hello"); let ref_raw = instance.as_ptr(); println!("instance value = {}", instance); println!("instance reference raw address = {:?}", ref_raw); println!(); let ref_string: &String = &instance; let ref_string = &instance; println!("ref_string value = {}", ref_string); println!("ref_string owned address = {:p}", ref_string); let ref_raw_string: *const u8 = ref_string.as_ptr(); assert_eq!(ref_raw, ref_raw_string); println!(); let ref_slice = unsafe { slice::from_raw_parts(&ref_raw, 5) }; dbg!(ref_slice); assert_eq!(&ref_raw, &ref_slice[0]); println!(); let u8_slice = unsafe { slice::from_raw_parts(ref_raw, 5) }; dbg!(u8_slice); #}
第二段代码:绑定字符串引用&String
类型的变量instance
println!();
let ref_string: &String = &instance;
let ref_string = &instance;
println!("ref_string value = {}", ref_string);
println!("ref_string owned address = {:p}", ref_string);
let ref_raw_string: *const u8 = ref_string.as_ptr();
这是唯一一段代码与前面实例代码不同的。在这一段代码里,前面两个let
绑定了字符串引用&String
类型的变量ref_string
,它们是完全等效的。特别需要指出的是,在关键词let
等式右边类型定义有时候是必要的,尽管这个实例可以省略,但是在上面实例里,就是必须的。
在这一段代码里,最后的let
绑定了*const u8
类型变量ref_raw_string
。
程序输出结果
该程序输出结果如下。比较上面实例结果,可以看到:在这个实例结果只有两个内存地址是一样的,而上面实例有三个内存地址是完全相同的。上面两个结构图就是示意这个结果。
[bin-hello/examples/use_raw_pointer_string.rs:32] slice = [
0x00007fac6ac03770,
0x0000000101bc4520,
0x0000000000000002,
0x0000000000000000,
0x00007fff5e066ca0,
]
[bin-hello/examples/use_raw_pointer_string.rs:41] slice = [
72,
101,
108,
108,
111,
]
───────┬─────────────────────────────────────────────────────────────────────────
│ STDIN
───────┼─────────────────────────────────────────────────────────────────────────
1 │
2 │ instance value = Hello
3 │ instance reference raw address = 0x7fac6ac03770
4 │
5 │ ref_string value = Hello
6 │ ref_string owned address = 0x7fff5e066ca0
7 │
8 │
───────┴─────────────────────────────────────────────────────────────────────────
引用、类型与原始指针解释
上面两个实例有什么不同?或者说,引用类型&String
与引用类型&str
的区别在哪里?
引用类型&String
是通过类型String
访问原始指针,而引用类型&str
是直接访问原始指针。引用类型&str
的方法是Rust语言借用系统的组成部分。这种方法更安全和更快速。我们把上面两个示意图合并到一起,如下所示:
从下面示意图可以看到,Rust语言的数据类型可以理解为是一种复杂的“引用”类型。引用类型主要是储存内存地址,而数据类型尽管也储存内存地址,但其重点是储存数据。
软件篋Cha(rs)
工具Cha(rs)
可以显示各种ASCII
和Unicode
字符/代码指针的名称和编码号。
# install
cargo install chars
# use
chars 'H'
软件篋ripgrep
软件篋ripgrep
是一款系统终端的搜索工具,类似于ack
和grep
。
# install
cargo install ripgrep
# use
ifconfig | rg netmask