Skip to content

Commit 39c666d

Browse files
committed
Add more impls of PartialEq and PartialOrd for strings
Currently, some combinations of `&String` and `&str` don't support comparison operators. For instance, this: ```rust fn main() { let s1 = String::from("hello"); let s2 = "world"; _ = s1 < s2; } ``` will fail with: ``` error[E0308]: mismatched types --> src/main.rs:4:14 | 4 | _ = s1 < s2; | -- ^^- help: try using a conversion method: `.to_string()` | | | | | expected `String`, found `&str` | expected because this is `String` ``` Other combinations only work because the compiler implicitly relies on impls on different reference types, and that makes such combinations fragile, breaking if any other impls of `PartialOrd` show up. Add some additional impls to make such cases work, and to improve robustness when adding other impls in the future.
1 parent 1c9837d commit 39c666d

File tree

5 files changed

+78
-31
lines changed

5 files changed

+78
-31
lines changed

library/alloc/src/string.rs

+38
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
4343
#![stable(feature = "rust1", since = "1.0.0")]
4444

45+
use core::cmp::Ordering;
4546
use core::error::Error;
4647
use core::iter::FusedIterator;
4748
#[cfg(not(no_global_oom_handling))]
@@ -2530,12 +2531,49 @@ macro_rules! impl_eq {
25302531

25312532
impl_eq! { String, str }
25322533
impl_eq! { String, &'a str }
2534+
impl_eq! { &String, str }
25332535
#[cfg(not(no_global_oom_handling))]
25342536
impl_eq! { Cow<'a, str>, str }
25352537
#[cfg(not(no_global_oom_handling))]
25362538
impl_eq! { Cow<'a, str>, &'b str }
25372539
#[cfg(not(no_global_oom_handling))]
25382540
impl_eq! { Cow<'a, str>, String }
2541+
#[cfg(not(no_global_oom_handling))]
2542+
impl_eq! { Cow<'a, str>, &String }
2543+
2544+
macro_rules! impl_ord {
2545+
($lhs:ty, $rhs: ty) => {
2546+
#[stable(feature = "rust1", since = "1.0.0")]
2547+
#[allow(unused_lifetimes)]
2548+
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
2549+
#[inline]
2550+
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
2551+
PartialOrd::partial_cmp(&self[..], &other[..])
2552+
}
2553+
}
2554+
2555+
#[stable(feature = "rust1", since = "1.0.0")]
2556+
#[allow(unused_lifetimes)]
2557+
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
2558+
#[inline]
2559+
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
2560+
PartialOrd::partial_cmp(&self[..], &other[..])
2561+
}
2562+
}
2563+
};
2564+
}
2565+
2566+
impl_ord! { String, str }
2567+
impl_ord! { String, &'a str }
2568+
impl_ord! { &String, str }
2569+
#[cfg(not(no_global_oom_handling))]
2570+
impl_ord! { Cow<'a, str>, str }
2571+
#[cfg(not(no_global_oom_handling))]
2572+
impl_ord! { Cow<'a, str>, &'b str }
2573+
#[cfg(not(no_global_oom_handling))]
2574+
impl_ord! { Cow<'a, str>, String }
2575+
#[cfg(not(no_global_oom_handling))]
2576+
impl_ord! { Cow<'a, str>, &String }
25392577

25402578
#[stable(feature = "rust1", since = "1.0.0")]
25412579
impl Default for String {

library/core/src/str/traits.rs

+32
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ impl PartialEq for str {
3030
}
3131
}
3232

33+
#[stable(feature = "rust1", since = "1.0.0")]
34+
impl PartialEq<&str> for str {
35+
#[inline]
36+
fn eq(&self, other: &&str) -> bool {
37+
self.as_bytes() == other.as_bytes()
38+
}
39+
}
40+
41+
#[stable(feature = "rust1", since = "1.0.0")]
42+
impl PartialEq<str> for &str {
43+
#[inline]
44+
fn eq(&self, other: &str) -> bool {
45+
self.as_bytes() == other.as_bytes()
46+
}
47+
}
48+
3349
#[stable(feature = "rust1", since = "1.0.0")]
3450
impl Eq for str {}
3551

@@ -48,6 +64,22 @@ impl PartialOrd for str {
4864
}
4965
}
5066

67+
#[stable(feature = "rust1", since = "1.0.0")]
68+
impl PartialOrd<&str> for str {
69+
#[inline]
70+
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
71+
Some(self.cmp(*other))
72+
}
73+
}
74+
75+
#[stable(feature = "rust1", since = "1.0.0")]
76+
impl PartialOrd<str> for &str {
77+
#[inline]
78+
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
79+
Some(self.cmp(&other))
80+
}
81+
}
82+
5183
#[stable(feature = "rust1", since = "1.0.0")]
5284
impl<I> ops::Index<I> for str
5385
where

tests/ui/binop/binary-op-suggest-deref.rs

-2
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ fn baz() {
6767
let string_ref = &owned;
6868
let partial = "foobar";
6969
_ = string_ref == partial[..3];
70-
//~^ERROR can't compare `&String` with `str` [E0277]
7170
_ = partial[..3] == string_ref;
72-
//~^ERROR can't compare `str` with `&String` [E0277]
7371
}
7472

7573
fn qux() {

tests/ui/binop/binary-op-suggest-deref.stderr

+3-27
Original file line numberDiff line numberDiff line change
@@ -271,32 +271,8 @@ note: an implementation of `PartialEq<&&{integer}>` might be missing for `Foo`
271271
LL | struct Foo;
272272
| ^^^^^^^^^^ must implement `PartialEq<&&{integer}>`
273273

274-
error[E0277]: can't compare `&String` with `str`
275-
--> $DIR/binary-op-suggest-deref.rs:69:20
276-
|
277-
LL | _ = string_ref == partial[..3];
278-
| ^^ no implementation for `&String == str`
279-
|
280-
= help: the trait `PartialEq<str>` is not implemented for `&String`
281-
help: consider dereferencing here
282-
|
283-
LL | _ = *string_ref == partial[..3];
284-
| +
285-
286-
error[E0277]: can't compare `str` with `&String`
287-
--> $DIR/binary-op-suggest-deref.rs:71:22
288-
|
289-
LL | _ = partial[..3] == string_ref;
290-
| ^^ no implementation for `str == &String`
291-
|
292-
= help: the trait `PartialEq<&String>` is not implemented for `str`
293-
help: consider dereferencing here
294-
|
295-
LL | _ = partial[..3] == *string_ref;
296-
| +
297-
298274
error[E0277]: no implementation for `i32 & str`
299-
--> $DIR/binary-op-suggest-deref.rs:78:17
275+
--> $DIR/binary-op-suggest-deref.rs:76:17
300276
|
301277
LL | let _ = FOO & (*"Sized".to_string().into_boxed_str());
302278
| ^ no implementation for `i32 & str`
@@ -309,14 +285,14 @@ LL | let _ = FOO & (*"Sized".to_string().into_boxed_str());
309285
`i32` implements `BitAnd`
310286

311287
error[E0277]: the size for values of type `str` cannot be known at compilation time
312-
--> $DIR/binary-op-suggest-deref.rs:78:17
288+
--> $DIR/binary-op-suggest-deref.rs:76:17
313289
|
314290
LL | let _ = FOO & (*"Sized".to_string().into_boxed_str());
315291
| ^ doesn't have a size known at compile-time
316292
|
317293
= help: the trait `Sized` is not implemented for `str`
318294

319-
error: aborting due to 24 previous errors
295+
error: aborting due to 22 previous errors
320296

321297
Some errors have detailed explanations: E0277, E0308, E0369.
322298
For more information about an error, try `rustc --explain E0277`.

tests/ui/inference/issue-72616.stderr

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ LL | if String::from("a") == "a".try_into().unwrap() {}
88
|
99
= note: cannot satisfy `String: PartialEq<_>`
1010
= help: the following types implement trait `PartialEq<Rhs>`:
11+
`&String` implements `PartialEq<Cow<'_, str>>`
12+
`&String` implements `PartialEq<String>`
13+
`&String` implements `PartialEq<str>`
14+
`String` implements `PartialEq<&String>`
1115
`String` implements `PartialEq<&str>`
1216
`String` implements `PartialEq<ByteStr>`
1317
`String` implements `PartialEq<ByteString>`
1418
`String` implements `PartialEq<Cow<'_, str>>`
15-
`String` implements `PartialEq<str>`
16-
`String` implements `PartialEq`
19+
and 2 others
1720
help: try using a fully qualified path to specify the expected types
1821
|
1922
LL | if String::from("a") == <&str as TryInto<T>>::try_into("a").unwrap() {}

0 commit comments

Comments
 (0)