5

为什么用 assert_eq 比较 string 和 &str 会成功?

 2 years ago
source link: https://tingfei.art/posts/compare_string_and_str/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

为什么用 assert_eq 比较 string 和 &str 会成功?

2020-11-20

昨天写代码的时候发现用 assert_eq 比较 string 和 &str 会成功,如下:

let s1 = String::from("hello");
let s2 = "hello";
assert_eq!(s1, s2);

凭直觉来讲,这不应该能通过,因为 string 和 &str 是两种类型,然后我觉得是 assert_eq 这个宏做了特殊处理,于是我又试了一下

let s1 = String::from("hello");
let s2 = "hello";
assert!(s1 == s2);

发现也没有问题,这说明和 assert_eq 没啥关系,于是我在 reddit 以及 Rust 中文社区提问,一个老哥的回答是:

String and &str cannot be compared at all, so Rust will look for ways to make the comparison possible. It will find that s1.deref() returns &str which obviously can be compared to &str (because Strings implement Deref<Target=str>) so it will automatically use that. This is called “deref coercion”.

简言之,他的理解是因为强制解引用多态(deref coercion)使得 s1 自动调用 deref() ,而其实现了 Deref <Target=str>,所以返回 &str , 然后就能与 s2 相比较了。逻辑非常清晰,我都已经道谢了,但是后面证实这是不正确的,另一位老哥也对这种说法进行了严谨地反驳:

这个说法是错的,跟 Deref 没有直接的关系,你可以这样写:

// assert macro will print using debug format
#[derive(Debug)]
struct S {
    inner: String,
}

impl S {
    pub fn new(inner: &str) -> Self {
        Self {
            inner: inner.to_owned(),
        }
    }
}

impl std::cmp::PartialEq<&str> for S {
    fn eq(&self, other: &&str) -> bool {
        return self.inner.eq(other);
    }
}

// comment for compile error
impl std::cmp::PartialEq<S> for &str {
    fn eq(&self, _other: &S) -> bool {
        return false;
    }
}

fn main() {
    let s = "hello";
    let string = String::from("hello");
    let fake_string = S::new("hello");
    assert_eq!(string, s);
    assert_eq!(fake_string, s);
    assert_eq!(s, fake_string);
}

这里 S 没有实现任何 deref,但是还是可以通过编译。把 assert_eq 宏展开之后是这样的:

match (&s, &fake_string) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    {
                        ::std::rt::begin_panic_fmt...

也就是说这里只调用了 lhs == rhs,只要实现了 PartialEq for Lhs 就可以了。 String 的文档里面两种顺序都实现了。

后面通过翻文档发现 String 和 str 确实互相实现了 PartialEq Trait,所以 String 和 &str 是可以直接比较的。

感谢 rustaceans 的热心解答。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK