Skip to content

Commit da49290

Browse files
committed
Auto merge of rust-lang#135536 - joshtriplett:str-impls, r=<try>
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. In particular, I'm hoping that adding these makes it possible to add comparisons with other stringy types without creating as many inference issues.
2 parents 419b3e2 + d972108 commit da49290

File tree

9 files changed

+109
-38
lines changed

9 files changed

+109
-38
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

src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
9898
let arg_snip = snippet(cx, arg_span, "..");
9999
let expr_snip;
100100
let eq_impl;
101-
if with_deref.is_implemented() {
102-
expr_snip = format!("*{arg_snip}");
103-
eq_impl = with_deref;
104-
} else {
101+
if without_deref.is_implemented() {
105102
expr_snip = arg_snip.to_string();
106103
eq_impl = without_deref;
104+
} else {
105+
expr_snip = format!("*{arg_snip}");
106+
eq_impl = with_deref;
107107
};
108108

109109
let span;

src/tools/clippy/tests/ui/iter_overeager_cloned.fixed

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ fn main() {
1313

1414
let _: Option<String> = vec.iter().chain(vec.iter()).next().cloned();
1515

16-
let _: usize = vec.iter().filter(|x| x == &"2").count();
16+
let _: usize = vec.iter().filter(|x| x == "2").count();
1717

1818
let _: Vec<_> = vec.iter().take(2).cloned().collect();
1919

2020
let _: Vec<_> = vec.iter().skip(2).cloned().collect();
2121

22-
let _ = vec.iter().filter(|x| x == &"2").nth(2).cloned();
22+
let _ = vec.iter().filter(|x| x == "2").nth(2).cloned();
2323

2424
let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
2525
.iter()

src/tools/clippy/tests/ui/iter_overeager_cloned.stderr

+20-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
2828
= note: `-D clippy::redundant-clone` implied by `-D warnings`
2929
= help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]`
3030

31+
error: taken reference of right operand
32+
--> tests/ui/iter_overeager_cloned.rs:16:42
33+
|
34+
LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
35+
| ^^^^^----
36+
| |
37+
| help: use the right value directly: `"2"`
38+
|
39+
= note: `-D clippy::op-ref` implied by `-D warnings`
40+
= help: to override `-D warnings` add `#[allow(clippy::op_ref)]`
41+
3142
error: unnecessarily eager cloning of iterator items
3243
--> tests/ui/iter_overeager_cloned.rs:18:21
3344
|
@@ -52,6 +63,14 @@ LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
5263
| |
5364
| help: try: `.nth(2).cloned()`
5465

66+
error: taken reference of right operand
67+
--> tests/ui/iter_overeager_cloned.rs:22:35
68+
|
69+
LL | let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
70+
| ^^^^^----
71+
| |
72+
| help: use the right value directly: `"2"`
73+
5574
error: unnecessarily eager cloning of iterator items
5675
--> tests/ui/iter_overeager_cloned.rs:24:13
5776
|
@@ -164,5 +183,5 @@ LL | let _ = vec.iter().cloned().any(|x| x.len() == 1);
164183
| |
165184
| help: try: `.any(|x| x.len() == 1)`
166185

167-
error: aborting due to 19 previous errors
186+
error: aborting due to 21 previous errors
168187

src/tools/clippy/tests/ui/op_ref.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn main() {
1818
let a = "a".to_string();
1919
let b = "a";
2020

21-
if b < &a {
21+
if b < a {
2222
println!("OK");
2323
}
2424

src/tools/clippy/tests/ui/op_ref.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ help: use the values directly
1111
LL | let foo = 5 - 6;
1212
| ~ ~
1313

14+
error: taken reference of right operand
15+
--> tests/ui/op_ref.rs:21:8
16+
|
17+
LL | if b < &a {
18+
| ^^^^--
19+
| |
20+
| help: use the right value directly: `a`
21+
1422
error: taken reference of right operand
1523
--> tests/ui/op_ref.rs:58:13
1624
|
@@ -35,5 +43,5 @@ LL | let _ = two + &three;
3543
| |
3644
| help: use the right value directly: `three`
3745

38-
error: aborting due to 4 previous errors
46+
error: aborting due to 5 previous errors
3947

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`.

0 commit comments

Comments
 (0)