为何值相同的&str、String、&String、&i32实例及引用比较结果均相等?
首先先看你提供的代码示例:
fn main() { let v1 = 5; let v2 = 5; let mut s = String::new(); s.push_str("a"); println!("{}", "a" == "a"); println!("{}", String::from("a") == String::from("a")); println!("{}", String::from("a") == s); println!("{}", &String::from("a") == &s); println!("{}", &v1 == &v2); }
你的疑问非常切中要害——明明这些实例的内存地址不同,为什么==比较结果都是相等?核心原因在于:Rust的==运算符不是直接比较内存地址,而是调用了类型实现的PartialEq trait,而绝大多数标准库类型都是按「值语义」来实现这个trait的。
咱们分场景具体解释:
1. &str 之间的比较
字符串字面量"a"属于&str类型,它的PartialEq实现逻辑是比较指向的字符串内容,而非指针本身的地址。哪怕两个&str来自不同的内存区域(比如一个是编译期字面量,一个是从String切片而来),只要字节内容完全一致,比较结果就是true。
2. String 实例之间的比较
String结构体本身包含三个字段:指向堆内存的指针、字符串长度、容量,但它的PartialEq实现会直接跳过这些结构体字段,转而比较堆内存里的实际字符串内容。所以哪怕两个String实例在堆上的内存地址完全不同,只要存储的字符一样,==就会返回true。
3. String 和 &str 的跨类型比较
这得益于Rust的**解引用强制转换(deref coercion)**机制:当你用==比较String和&str时,String会被自动解引用为&str,最终还是回到了字符串内容的比较,自然结果相等。
4. &String 引用之间的比较
&String的PartialEq实现会先把引用解引用为&str,再比较内容。换句话说,它比较的是引用指向的String实例的实际值,而非两个引用本身的内存地址。所以哪怕两个&String指向不同的String实例,只要实例内容相同,比较结果就是true。
5. &i32 引用之间的比较
像整数这种基础Copy类型,&i32的PartialEq实现逻辑是比较引用指向的整数值,而非指针地址。毕竟对于简单数值类型,比较值本身才是符合直觉的行为,Rust默认就这么设计了。
额外补充:如果想比较内存地址怎么办?
如果确实需要判断两个实例的内存地址是否相同,可以使用std::ptr::eq函数,它会直接比较指针的地址:
let s1 = String::from("a"); let s2 = String::from("a"); println!("{}", std::ptr::eq(&s1, &s2)); // 输出false,两个String实例地址不同 println!("{}", std::ptr::eq(s1.as_str(), s2.as_str())); // 输出false,动态分配的String切片地址不同
内容的提问来源于stack exchange,提问作者Wynell

