我正在尝试了解 Rust 内存的来龙去脉。当在函数内部创建向量然后返回时,是返回引用还是复制整个向量?
示例:
use std::io;
fn line_to_ints() -> Vec<u32> {
let mut line = String::new();
io::stdin()
.read_line(&mut line)
.expect("Failed to read line");
return line
.split(" ")
.map(|x| x.parse().expect("Not an integer!"))
.collect();
}
对于所有其他非原始数据类型,这里的返回行为也相同吗?
与有没有办法返回对函数中创建的变量的引用?,我想更多地了解幕后发生的事情。该问题的答案并没有明确说明是否创建向量然后将其复制到新位置,或者返回指针的所有权我理解向量是在堆上创建的,所以我想涉及到一个指针。
是返回的参考
不。不可能是因为一旦函数结束就没有没有什么可以引用。 有什么方法可以返回对函数中创建的变量的引用吗?。
对此进行了详细介绍。整个向量都被复制了
是的,但可能不是你的意思。 A
Vec
基本上定义为
struct Vec<T> {
capacity: usize,
length: usize,
data: *mut T,
}
从语义上讲,这 3 个指针大小的字段已从函数“移动”到调用者。向量包含的 N 个元素不会被复制。 在实现方面,编译器/优化器可以从一大堆技巧中进行选择:
实际上复制所有三个字段
对于所有其他非原始数据类型,这里的返回行为也相同吗?
是的。 Rust 的数据类型都被同等对待。原始与非原始对于语言的语义来说没有任何意义。
另请参阅:
Vec 结构本身,这是一个固定大小的结构,由一个指针和两个指针大小的整数组成,分别表示大小和容量。所以它的大小是三个指针(32 位上 12 个字节,64 位上 24 个字节)。
Rust 语言没有具体指定 vec 结构如何移动。然而,在大多数情况下,rustc 遵循目标平台的标准调用约定。 amd64 和 aarch64 都在寄存器中返回小结构,但它们对“小结构”的正常定义是大小最多为两个指针的结构。除此之外,他们转向了“大型结构”的策略,即用指针替换它们。
为了演示这一点,我在 godbolt 上构建了以下代码。
#[inline(never)]
pub fn vecofints(v: Vec<i32>) -> Vec<i32> {
return v;
}
在 x86-64 上这导致了。
example::vecofints::h4b8b50ca2c3019a3:
mov rax, rdi
mov rcx, qword ptr [rsi + 16]
mov qword ptr [rdi + 16], rcx
movups xmm0, xmmword ptr [rsi]
movups xmmword ptr [rdi], xmm0
ret
在这种情况下,我们可以看到,参数和结果都作为指针传递,指向参数的指针在 rsi(第一个参数传递寄存器)中传递,指向结果的指针在 rdi(第二个参数传递寄存器)中传递。参数传递寄存器)。
在 aarch64 上这导致了。
example::vecofints::hee34a085ad28128f:
ldr q0, [x0]
ldr x9, [x0, #16]
str q0, [x8]
str x9, [x8, #16]
ret
这非常相似,除了 aarch64 没有在参数传递寄存器之一中传递返回值的隐藏指针,而是有一个专用寄存器用于此目的。
可以实现一种类似 vec 的类型,其中 Vec 结构本身只有一个大小的指针,其中有一些通常具有“thinvec”之类的名称。