It's not exactly an idiomatic MVCE, but it should illustrate the question. Given the following code:
fn foo() -> Result<String, i32> {
return Ok("world".to_string());
}
fn bar() -> Result<String, i32> {
let mut value = String::new();
value.push_str(&try!(foo())); // this line here
return Ok("Hello ".to_string() + &value);
}
fn main() {
match bar() {
Ok(message) => println!("{}", message),
_ => return,
}
}
Rust returns the error:
<std macros>:3:43: 3:46
error: mismatched types:
expectedstr
,
foundcollections::string::String
(expectedstr
,
found structcollections::string::String
) [E0308]<std macros>:3 $ crate:: result:: Result:: Ok ( val ) => val , $ crate:: result:: Result::
<std macros>:1:1: 6:48
note: in expansion oftry!
<std macros>:3:43: 3:46
help: runrustc --explain E0308
to see a detailed explanation
error: aborting due to previous error
If I instead capture the result of try!
and separately apply &
to the result, it works (and prints out Hello world
):
fn foo() -> Result<String, i32> {
return Ok("world".to_string());
}
fn bar() -> Result<String, i32> {
let mut value = String::new();
let foo_result = try!(foo()); // capture the result of try!
value.push_str(&foo_result); // now use the result with &
return Ok("Hello ".to_string() + &value);
}
fn main() {
match bar() {
Ok(message) => println!("{}", message),
_ => return,
}
}
Why does let foo_result = try!(foo()); value.push_str(&foo_result);
work but value.push_str(&try!(foo()));
not? From my naive perspective, they appear to be equivalent, so I'm not sure what key part of Rust I'm not understanding.
It seems that the compiler handles the coercion of a block differently. try!()
is expanded to a match
block, and the compiler fails to auto-deref it. Your problem can be abbreviated like below:
fn f(_: &str) {}
fn main() {
let x = "Akemi Homura".to_owned();
f(&x); // OK
f(&(x)); // OK
f(&{x}); // Error
}
I think this is a bug of the compiler. As stated in RFC 401, the compiler should be able to coerce blocks with appropriate types.
blocks, if a block has type
U
, then the last expression in the block (if it is not semicolon-terminated) is a coercion site toU
. This includes blocks which are part of control flow statements, such asif
/else
, if the block has a known type.
As a workaround, I recommend you to convert String
into &str
directly, using &*try()
or &try()[..]
. Both have the same meaning, although I prefer the former slightly.
I opened an issue to track this. https://github.com/rust-lang/rust/issues/26978
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With