Rust学习中需要注意的一些点——持续更新

1.对可Copy类型数据,引用时,不会拷贝一份数据到栈中;复制时,会在其他位置拷贝一份数据到栈中
use std::ptr::addr_of;
use std::mem::size_of_val;
#[allow(unused)]
fn main() {
  let x: i8 = 5;
  let y: &i8 = &x;//引用
  let z: i8 = x;//复制
  let z1: i8 = x;//复制
  println!("{:?} {:?} {:?} {:?}",addr_of!(x),addr_of!(y),addr_of!(z),addr_of!(z1));
  println!("{} {} {} {}", size_of_val(&x),size_of_val(&y),size_of_val(&z),size_of_val(&z1)); // 输出 value 占用的字节数
}

输出:

0x7ffeb7efcfc7 0x7ffeb7efcfc8 0x7ffeb7efcfd6 0x7ffeb7efcfd7
1 8 1 1
  • 已知,i8类型的大小为1B,&i8 是对 i8 类型的不可变引用,引用的大小与它所引用的数据类型的大小无关,而是取决于平台的指针大小。在 32 位架构上,&i8 的大小通常是 4 字节,而在 64 位架构上,大小是 8 字节。这里我的电脑是64位,所以这里&i8类型大小为8B。
  • addr_of!函数的输出可知x,y,z,z1在内存中的地址
  • size_of_val(&value)函数可以看出具体值而不是某一类型在内存中所占字节数大小
  • 综上所述,可以看出,对于可Copy的数据类型,当用引用&时,会在栈中生成一个指针指向原数据,而不会拷贝一份数据,指针大小为4B或者8B;当复制时,会在栈中拷贝一份数据
  • 另外,从addr_of!函数的输出看出,y和z在内存中的位置并没有挨着,这应该是为了符合边界对齐的原则
2.在 Rust 中,当你通过索引访问数组或向量的元素时,你得到的是该元素的不可变引用

数组和向量是固定大小的集合,并且它们的元素存储在栈上,它们提供的索引操作符返回的是元素的不可变引用。

并且在Rust语言圣经中,在Vector动态数组的学习中,该书给了两种方式实现从Vector中读取元素

  • 通过下标索引访问
  • 使用 get 方法
let v = vec![1, 2, 3, 4, 5];

let third: &i32 = &v[2];
println!("第三个元素是 {}", third);

match v.get(2) {
    Some(third) => println!("第三个元素是 {third}"),
    None => println!("去你的第三个元素,根本没有!"),
}

和其它语言一样,集合类型的索引下标都是从 0 开始,&v[2] 表示借用 v 中的第三个元素,最终会获得该元素的引用。而 v.get(2) 也是访问第三个元素,但是有所不同的是,它返回了 Option<&T>,因此还需要额外的 match 来匹配解构出具体的值。

对于不可Copy的类型呢,这里用数组中放String类型举例:

#[allow(unused)]
fn main() {
  let x = "x".to_string();
  let y = x;//成功执行
  
  let arr = [1, 2];
  let (first, second) = (arr[0], arr[1])//成功执行
  
  let arr = ["1".to_string(), "2".to_string()];
  let (first, second) = (arr[0], arr[1]);//失败
}

报错如下:

error[E0508]: cannot move out of type `[String; 2]`, a non-copy array
  --> src/main.rs:10:34
   |
10 |   let (first, second) = (arr[0], arr[1]);
   |                                  ^^^^^^
   |                                  |
   |                                  cannot move out of here
   |                                  move occurs because `arr[_]` has type `String`, which does not implement the `Copy` trait

可以看出,let y = x可以正确执行,这里是将x的所有权转移给y,之后x不能再用。

当arr = [1, 2]时,let (first, second) = (arr[0], arr[1]);没有&arr[0],&arr[1]也能成功执行。

当arr = [“1”.to_string(), “2”.to_string()]时,我们来分析let (first, second) = (arr[0], arr[1]); 这里arr[0]通过索引访问数组元素,期望得到&String类型,实际上得到String类型,那么这条语句不就是相当于把两个String类型数据的所有权给first和second嘛,转移所有权也不难嘛,前两条代码不也执行的好好的,想法很美好,实际上不能成功。

Rust为了安全考虑,通过索引访问数组元素希望得到的是String的不可变引用(&String),而不是String的所有权。Rust不允许直接从一个不可变引用中复制所有权,因为引用本身并不拥有数据的所有权。因此会发生冲突报错,报错提示我们String没有实现Copy特征,这同样也是为什么当arr = [1,2]时,let (first, second) = (arr[0], arr[1]);没有用&arr[0],&arr[1],但是仍然能够成功执行的原因,因为整形类型实现了Copy特征,整形类型没有所有权的转移,可以直接在栈中深拷贝数据,不会有安全问题。

最后,在Rust中,如果你有一个包含String类型元素的动态数组(通常是通过Vec<String>来表示),你可以通过几种方式来转移元素的所有权:

使用 remove 方法:
remove 方法可以从向量中移除一个元素,并返回该元素的所有权。你可以指定要移除的索引,例如移除第一个元素:

fn main(){
    let mut v = vec!["hello".to_string(), "world".to_string()];
    let removed_element = v.remove(0); // 移除第一个元素,并获取所有权
    println!("Removed element: {}", removed_element);
}

使用模式匹配进行解构:
如果你知道向量至少有一个元素,你可以使用模式匹配来转移所有权:

fn main(){
    let mut v = vec!["hello".to_string(), "world".to_string()];
    let first_element = match v.pop() {
    Some(s) => s, // pop 方法移除并返回向量中的最后一个元素
    None => panic!("Vector is empty"),
    };
    println!("First element: {}", first_element);
}
  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值