Deref coercion from String to &str doesn't seem to always work
This code compiles fine:
fn main() {
let x = "a".to_string();
let y: String = ["b", &x[..], "c"].concat();
println!("{}", y);
}
but this code doesn't
fn main() {
let x = "a".to_string();
// Doesn't work
let y: String = ["b", &x, "c"].concat();
println!("{}", y);
}
error output:
<anon>:4:25: 4:27 error: mismatched types:
expected `&str`,
found `&collections::string::String`
(expected str,
found struct `collections::string::String`) [E0308]
<anon>:4 let y: String = ["b", &x, "c"].concat();
^~
I'd expect to not have to sometimes write &foo[..] and sometimes &foo to get a &str out a String. The longer form is too verbose, and the inconsistency of having to sometimes use one over the other seems like a needless user mental model cost, especially because it's not obvious to me at all when I should use one form over the other.
rustc version: rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-21)
cc @eddyb
Jorge Aparicio at 2015-02-21 22:44:56
Another example, with
Vector<T>and[T]. This works:fn main() { let x = vec![97u8]; assert_eq!(b"a", &x[..]); }But this fails to compile:
fn main() { let x = vec![97u8]; assert_eq!(b"a", &x); }Output:
<std macros>:5:10: 5:35 error: the trait `core::cmp::PartialEq<collections::vec::Vec<u8>>` is not implemented for the type `[u8]` [E0277] <std macros>:5 if ! ( ( * left_val == * right_val ) && ( * right_val == * left_val ) ) { ^~~~~~~~~~~~~~~~~~~~~~~~~ <std macros>:1:1: 9:39 note: in expansion of assert_eq! <anon>:3:3: 3:24 note: expansion site <std macros>:5:43: 5:68 error: the trait `core::cmp::PartialEq<[u8]>` is not implemented for the type `collections::vec::Vec<u8>` [E0277] <std macros>:5 if ! ( ( * left_val == * right_val ) && ( * right_val == * left_val ) ) { ^~~~~~~~~~~~~~~~~~~~~~~~~ <std macros>:1:1: 9:39 note: in expansion of assert_eq! <anon>:3:3: 3:24 note: expansion site error: aborting due to 2 previous errors playpen: application terminated with error code 101I have tons of other examples in my codebase. Like, 30+.
Val Markovic at 2015-02-21 23:13:55
(FWIW, that second example could be written as
assert_eq!(b"a", vec![97u8])becausePartialEqis implemented between&[u8]andVec<u8>, no need for deref coercions nor slicing syntax)Jorge Aparicio at 2015-02-21 23:35:49
The problem also appears here:
fn main() { let rs = "toto"; let s = rs.to_string(); // That works as expected, &s coerces s to type &str fn titi(s:&str) { assert_eq!(s,"toto");}; titi(&s); // That also works, same coercion happens let cs:&str = &s; assert_eq!(cs,rs); // The following expression fails to run, as though the coercion did not happen let cs = &s; assert_eq!(cs,rs); }yann-ledu at 2015-02-26 13:09:49
This is somewhat similar to https://github.com/rust-lang/rust/issues/16864.
I think @nrc was doing some work on coercions recently?
Huon Wilson at 2016-01-08 06:35:29
I can confirm the issue is still present on
rustc 1.8.0-nightly (57c357d89 2016-02-16). My testcase still repros.Val Markovic at 2016-02-21 01:37:47
Both arrays and the expansion of
assert_eqfail to trigger coercions. The former is easier to fix, try changing this call to be tocheck_expr_coercable_to_typeinstead. That should make it so the first array element with even a partially inferrable type triggers coercions on subsequent array elements. The perfect solution would involve smarter unification such that&Stringfollowed by&strgoes back and coerces the&String(or @nikomatsakis' order-independent type-checking).Eduard-Mihai Burtescu at 2016-02-21 04:24:23
Interesting developments: the first test case I posted now compiles fine.
fn main() { let x = "a".to_string(); // Doesn't work let y: String = ["b", &x, "c"].concat(); println!("{}", y); }But the second test case fails compilation:
fn main() { let x = vec![97u8]; assert_eq!(b"a", &x); }Here's the error message:
error[E0277]: the trait bound `[u8; 1]: std::cmp::PartialEq<std::vec::Vec<u8>>` is not satisfied --> <std macros>:5:6 | 5 | if ! ( * left_val == * right_val ) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ foo.rs:3:3: 3:24 note: in this expansion of assert_eq! (defined in <std macros>) | = help: the following implementations were found: = help: <[A; 0] as std::cmp::PartialEq<[B; 0]>> = help: <[A; 0] as std::cmp::PartialEq<[B]>> = help: <[B] as std::cmp::PartialEq<[A; 0]>> = help: <[A; 0] as std::cmp::PartialEq<&'b [B]>> = help: and 228 others = note: required because of the requirements on the impl of `std::cmp::PartialEq<&std::vec::Vec<u8>>` for `&[u8; 1]` error: aborting due to previous errorrustc version:
rustc 1.12.0 (3191fbae9 2016-09-23).Val Markovic at 2016-10-02 03:16:37
such that
&Stringfollowed by&strgoes back and coerces the&StringFWIW this is what was implemented and why the array case works now.
Eduard-Mihai Burtescu at 2016-10-02 03:38:43
Here is another case with a trait, wich fails to compile:
fn main() { concat("nice", "cooel"); concat("nice".to_string(), "cooel"); let yop = "nice".to_string(); concat(&yop, "cooel"); // need to add manually "as &str" } pub fn concat<S: Into<String>>(path: S, suffix: &str) -> String { path.into() + suffix }PSeitz at 2018-02-14 10:44:36
Also ran into this, in a very simple case:
works:
fn prompt_and_validate(msg: &str, options: &[&str]) { let reply = prompt(msg); let r: &str = &reply; if options.contains(&r) { } }doesn't work:
fn prompt_and_validate(msg: &str, options: &[&str]) { let reply = prompt(msg); let r = &reply; if options.contains(&r) { } }or in other words, double deref is not working automatically:
fn prompt_and_validate(msg: &str, options: &[&str]) { let reply = prompt(msg); if options.contains(&&reply) { } }with
mismatched types expected str, found struct `std::string::String` note: expected type `&&str` found type `&&std::string::String`(workaround is
&reply.as_str())Michal Srb at 2019-04-21 22:44:58
This still seems to fail today in Rust 1.55:
fn main() { let cmd = "some command".split_whitespace().collect::<Vec<_>>(); match &cmd { ["some", sub] => println!("some {}", sub), ["quit"] => println!("bye!"), _ => println!("oops"), } }with:
error[E0529]: expected an array or slice, found `Vec<&str>` --> src/main.rs:13:9 | 13 | ["some", sub] => println!("some {}", sub), | ^^^^^^^^^^^^^ pattern cannot match with input type `Vec<&str>` error[E0529]: expected an array or slice, found `Vec<&str>` --> src/main.rs:14:9 | 14 | ["quit"] => println!("bye!"), | ^^^^^^^^ pattern cannot match with input type `Vec<&str>` For more information about this error, try `rustc --explain E0529`. error: could not compile `playground` due to 2 previous errorsExchanging
&cmdfor&*cmdor&cmd[..]makes it work. Is this intended or really a bug that the coercion does not trigger here?Rogério Almeida at 2021-09-17 21:24:49
It's a missing feature: we could try to figure out some kind of "type skeleton" from the patterns, before type-checking the expression being
matched, but we don't today.That is, today the information flows only the other way around: from the expression to the patterns.
Eduard-Mihai Burtescu at 2021-09-20 21:53:14
we could try to figure out some kind of "type skeleton" from the patterns, before type-checking the expression being matched, but we don't today
@eddyb Is this still something we might want to do?
pierwill at 2023-08-29 19:38:45