Rust引用/借用底层原理:栈指针与堆指针解析
核心疑问
我从概念上理解Rust的借用机制:借用是不拥有所有权的值,必须由借用者释放后归还给所有者。但我不清楚引用的本质:看到资料称Rust引用不会进行堆分配,可它本质又是指针,甚至能直接转换为原始堆指针,这让我困惑——我原以为指针和栈分配是互斥的,这一理解是否有误?是否是资料有误?同时我希望了解以下代码的内存布局,并纠正我对另一段代码内存布局的错误理解。
关键概念澄清:指针≠堆分配
你的核心误解在这里:指针和栈分配完全不互斥。指针只是一个存储内存地址的变量,它本身可以存在栈上,和它指向的内存位置(栈/堆)是两码事。Rust的引用本质就是栈上的“薄指针”——它只存储目标值的内存地址,没有任何堆分配操作,和C语言里的栈指针是一个道理。
第一个示例代码的内存布局解析
先看这段代码:
// owned value let some_val = 1; // reference let some_ref = &some_val; // pointer let some_ptr = some_ref as *const i32; // prints "1 1 1" as expected unsafe { println!("{} {} {}", some_val, some_ref, *some_ptr); }
它的内存布局(以64位系统为例):
some_val:栈上分配的i32变量,占用4字节内存,存储值1,假设它的地址是0x1000some_ref:栈上分配的&i32引用,占用8字节内存,存储的是some_val的地址0x1000——完全没有堆分配,只是个栈上的指针some_ptr:栈上分配的*const i32原始指针,同样占用8字节,存储的也是0x1000——它和some_ref的底层数据完全一致,只是绕过了Rust的借用检查规则- 打印时,
some_ref会被自动解引用显示目标值,*some_ptr在unsafe块中手动解引用,所以三者都输出1
第二个示例代码的内存布局错误纠正
再看这段你有误解的代码,逐条纠正:
// load const 1 and store as some_val let mut some_val = 1; // load 1 onto the stack and invoke do_something do_something(some_val); // still load 1 onto the stack and invoke do_something, but ownership is not given to do_something() do_something(&some_val); // still load 1 onto the stack and invoke do_something & ownership is not given, but the borrow checker also makes sure that some_val is not mutably borrowed until do_something is done with &mut some_val do_something(&mut some_val); // allocate a copy of some_val to the heap, load a raw pointer to the new memory location onto the stack, and invoke do_something on the raw pointer. We still own some_val do_something(&some_val as *const i32);
let mut some_val = 1;:some_val是栈上分配的可变i32变量,直接存储值1,没有任何堆操作。do_something(some_val);:
因为i32实现了Copytrait,这里会把some_val的值复制一份到栈的函数参数区域,然后调用do_something——不是“加载1到栈”这么笼统,而是复制值;原some_val依然保留所有权(因为Copy类型移动时会自动复制)。do_something(&some_val);:
不是把1加载到栈上,而是把**指向栈上some_val的引用(指针)**放到函数参数栈区域。do_something接收的是这个指针,通过它访问原栈上的some_val,没有复制值,也没有堆分配——所有权确实没转移,但内存上传递的是指针而非值。do_something(&mut some_val);:
和不可变引用逻辑一致,只是传递的是可变引用指针,放在参数栈区域。借用检查器确保这段时间内some_val没有其他引用,但内存上依然是传递栈上的指针,没有复制值或堆分配。do_something(&some_val as *const i32);:
这里完全没有堆分配!只是把&some_val这个栈上的引用转成原始指针,然后传递这个原始指针(依然在栈参数区域)。原始指针指向的还是栈上的some_val,没有复制值到堆的操作。
内容的提问来源于stack exchange,提问作者Jam

