Rust中简单类型何时及为何优先按值或引用传递?(附示例)
Rust中简单类型按值与引用传递的选择分析
先看示例结构体及Getter方法:
use chrono::{DateTime, Utc}; pub type Bytes = [u8; 16]; #[derive(Clone, Copy)] pub struct Uuid(Bytes); struct X { v_str: String, v_num: i32, v_bool: bool, v_uuid: Uuid, v_date: DateTime<Utc>, } impl X { pub fn v_str_ref(&self) -> &str { &self.v_str } pub fn v_str_val(&self) -> String { self.v_str.clone() } pub fn v_num_ref(&self) -> &i32 { &self.v_num } pub fn v_num_val(&self) -> i32 { self.v_num } pub fn v_bool_ref(&self) -> &bool { &self.v_bool } pub fn v_bool_val(&self) -> bool { self.v_bool } pub fn v_uuid_ref(&self) -> &Uuid { &self.v_uuid } pub fn v_uuid_val(&self) -> Uuid { self.v_uuid } pub fn v_date_ref(&self) -> &DateTime<Utc> { &self.v_date } pub fn v_date_val(&self) -> DateTime<Utc> { self.v_date } }
下面针对不同类型逐一分析传递方式的选择:
1. 栈上小类型:i32、bool
这类类型的大小通常等于或小于指针大小(64位系统下指针为8字节,i32是4字节、bool是1字节),优先按值传递:
- 性能上:直接传值的开销比传引用更低——引用需要额外存储指针、执行解引用操作,反而会增加指令开销。
- 使用上:避免了借用检查器的约束,不需要处理生命周期、可变/不可变借用冲突等问题,代码更简洁。
2. 堆分配类型:String
这类类型的数据存储在堆上,结构体本身仅包含指针、长度、容量(3个机器字),优先传引用(&str):
- 性能上:传递引用只需要复制8字节的指针,避免了克隆整个堆上字符串的昂贵开销。
- 场景例外:当需要获得数据的所有权(比如要修改字符串、长期持有且不想受原结构体生命周期限制)时,才需要克隆并按值传递。
3. 实现Copy trait的自定义类型:Uuid
示例中的Uuid标注了#[derive(Clone, Copy)],意味着它是按位复制的类型,内部的[u8;16]是固定大小的栈数组,复制成本极低(仅16字节的内存拷贝),因此遵循和i32、bool一样的规则:优先按值传递。
- 传引用反而画蛇添足:不仅不会提升性能,还会引入借用检查的额外约束,完全没必要。
4. 非Copy的中等大小类型:DateTime
DateTime<Utc>属于这类:它的数据完全存储在栈上(通常包含一个i64时间戳和Utc时区标记,总大小约16字节),但默认没有实现Copy trait,需要通过Clone复制。针对这类类型:
- 只读场景:优先传引用
&DateTime<Utc>,避免Clone带来的内存拷贝开销。 - 需要所有权或简化代码:如果复制的开销(16字节拷贝)远低于处理借用规则的复杂度,也可以直接按值传递(示例中直接返回
self.v_date是假设它可Copy,实际若需Clone则调用clone())。 - 和String的区别:DateTime没有堆分配,复制开销远小于克隆String,所以传值的场景比String更多,不需要像String那样严格优先传引用。
针对你的疑问总结
Uuid因为是Copy类型,和数值、布尔类型规则一致,优先按值传递。DateTime<Utc>和String规则不同:它是栈上的中等大小类型,复制开销低,传值场景更多;而String是堆分配类型,必须优先传引用以避免昂贵的克隆。
内容的提问来源于stack exchange,提问作者ZiiMakc
相关产品推荐
相关产品推荐

