Description
The following code produces an error message that doesn’t at all explain the actual problem.
fn foo() -> &'static str {
let identity = |x| x;
let result = identity("result!");
let local_string: String = "local".into();
let _ = identity(&local_string);
result
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local variable `local_string`
--> src/lib.rs:7:5
|
5 | let _ = identity(&local_string);
| ------------- `local_string` is borrowed here
6 |
7 | result
| ^^^^^^ returns a value referencing data owned by the current function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
In particular looking at this statement – “returns a value referencing data owned by the current function” – that’s just not what is going on here.
What really is going on here (as far as I can tell) is that identity
is inferred as some closure implementing Fn(A) -> A
for A
to be determined, then the first call identity("result!")
tells the type inference that A
is &'a str
for some 'a
to be determined, and finally identity(&local_string)
already knows to apply unsized coercion and typechecks nailing 'a
down to the lifetime of local_string
. This means result
is an &'a str
with that same lifetime.
A bit less confusing of an error message but still suboptimal IMO is what you get when trying to use a closure truly polymorphically:
fn foo() {
let identity = |x| x;
identity(1u8);
identity(1u16);
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:4:14
|
4 | identity(1u16);
| ^^^^ expected `u8`, found `u16`
|
help: change the type of the numeric literal from `u16` to `u8`
|
4 | identity(1u8);
| ^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
I think this could be improved by a message explaining where the expected type u8
comes from.
Back to the original example.. in my opinion it would be nice if the example would not error at all. After all the following does work:
fn str_closure<F: Fn(&str) -> &str>(f: F) -> F { f }
fn foo() -> &'static str {
let identity = str_closure(|x| x);
let result = identity("result!");
let local_string: String = "local".into();
let _ = identity(&local_string);
result
}
However, this again doesn’t work:
fn str_closure<F: Fn(&str) -> &str>(f: F) -> F { f }
fn foo() -> &'static str {
let closure = |x| x;
let identity = str_closure(closure);
let result = identity("result!");
let local_string: String = "local".into();
let _ = identity(&local_string);
result
}
Finally, on the topic of trying to get more polymorphism out of closures than they support, I ran into this error message (which I could move into a separate issue if you guys think that it’s completely unrelated):
fn f<A>() -> A { unimplemented!() }
fn foo() {
let _ = f;
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0282]: type annotations needed for `fn() -> A {f::<A>}`
--> src/lib.rs:3:13
|
3 | let _ = f;
| - ^ cannot infer type for type parameter `A` declared on the function `f`
| |
| consider giving this pattern the explicit type `fn() -> A {f::<A>}`, where the type parameter `A` is specified
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
Which is just weird, because: What is this fn() -> A {f::<A>}
syntax supposed to mean?
@rustbot modify labels to T-compiler, T-lang, A-diagnostics, D-incorrect, D-terse, D-papercut, C-bug, C-enhancement, A-closures, A-inference.