This question is about how to read the Rust documentation and improve my understanding of Rust so as to understand how to address this specific compiler error.
I've read the tokio docs and experimented with many of the examples. In writing my own code, I frequently run into compiler errors that I don't understand and often found I can fix the code, but don't understand why specific syntax is needed.
I reproduced with a very simple example based on tokio's hello world:
use futures::Future;
use tokio::net::TcpStream;
use tokio::prelude::*;
fn main() {
let addr = "127.0.0.1:6142".parse().unwrap();
let client = TcpStream::connect(&addr).and_then(|stream| {
println!("created stream");
// Process stream here.
// Ok(())
});
}
The above code is incorrect, requiring the commented out Ok(). I know that this is true, but not exactly why. This is perhaps the other half of a prior question How do I interpret the signature of read_until and what is AsyncRead + BufRead in Tokio? -- now I understand closures better, but can't quite parse the docs to understand the expected return value.
When I attempt to compile the incorrect code above, I get the following error:
error[E0277]: the trait bound `(): futures::future::Future` is not satisfied
--> tokio-chat-client/src/main.rs:8:42
|
8 | let client = TcpStream::connect(&addr).and_then(|stream| {
| ^^^^^^^^ the trait `futures::future::Future` is not implemented for `()`
|
= note: required because of the requirements on the impl of `futures::future::IntoFuture` for `()`
There are two parts to my question:
What is the error message trying to tell me?
How would I use the docs for and_then to understand the expected return value?
The docs for and_then state that:
fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F> where
F: FnOnce(Self::Item) -> B,
B: IntoFuture<Error = Self::Error>,
Self: Sized,
This means that:
Your closure must accept an argument of type Self::Item and return some type B
The type B returned by your closure must be convertible into a future.
And if that future returns an error, then that error must have type Self::Error.
Moreover, if you look at the doc for IntoFuture, you will see that it is implemented for Result, so it works for Ok(()), but that it is not implemented for (), so it doesn't work if your closure returns nothing.
Basically the closure you pass to and_then has the wrong type. It expects:
F: FnOnce(Self::Item) -> B
But you give it a closure of unit type, i.e. returns no value. Hence the error.
That said, the rustc error message isn't optimal here. It would be much better if it reads:
let client = TcpStream::connect(&addr).and_then(|stream| {
println!("created stream");
// error: mismatched types: expected `IntoFuture` but found `()`
});
The rust-lang project has this ticket to track the progress on said diagnostics issue.
Related
I don't understand something about the feature that makes it possible to coerce a tuple struct to a function as in:
struct MyType(u8);
let optional_mytype: Option<MyType> = Some(12).map(MyType);
// ^^^^^^ here, MyType is treated as a function
In the example above, no lifetimes are involved: everything is easy. However, when the structure has a generic lifetime bound, I'm having troubles defining the exact signature of the function it would coerce to.
Here's a MRE of something I've attempted to do, and that doesn't work (sandbox link):
struct MyStruct<'a>(&'a ());
// This looks, to me, like the signature the "constructor" should have
type MyStructConstructor = for<'a> fn(&'a ()) -> MyStruct<'a>;
/// Coercion problem here
fn some_code() {
let mut constructor: MyStructConstructor = MyStruct;
}
and the error given by the type-checker:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:7:48
|
7 | let mut constructor: MyStructConstructor = MyStruct;
| ^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'a> fn(&'a ()) -> MyStruct<'a>`
found fn pointer `fn(&()) -> MyStruct<'_>`
And I honestly don't understand why my constructor type alias isn't valid, and why the type suggested by the error message is much more lax than it should be. I mean, the lifetime <'a> of my structure should be exactly equal to that of the unit reference that is contained by the structure, not a random one.
Here's a more concrete (still minimal) example of what I'm actually trying to do, if having context helps you.
Thanks in advance
In fact, the error message might be a bit misleading. '_ does not mean any lifetime goes, it just means a lifetime I haven't named explicitly, just as writing &() (which is the same as &'_ ()). Rust does that because that's not the issue.
To understand, let's actually annotate implicit generic lifetimes in your code:
fn some_code() {
let constructor: MyStructConstructor = MyStruct<'_>;
}
Notice how something is wrong? On the right hand side, you have something whose type is generic over a lifetime. On the left hand side, you don't.
In fact, a more appropriate constructor signature for MyStruct would be
struct MyStruct<'a>(&'a ());
type MyStructConstructor<'a> = fn(&'a ()) -> MyStruct<'a>;
fn some_code() {
let constructor: MyStructConstructor = MyStruct;
}
leaving the lifetime implicit — see the playground.
This compiles.
So now you may understand why MyStructConstructor was more generic than MyStruct: because for MyStruct, you have to specify a lifetime to begin with, that is, the actual type is, for a given 'a, MyStruct<'a>. Then, with that constructor, you are only capable of building objects of type MyStruct<'a>, given &'a (). On the other hand, with MyStructConstructor, given a reference to a () with any lifetime, you would be able to build a MyStruct of the same lifetime, which is more general.
Let's say I want to implement a conversion on a reference. In this case, it's a conversion from &f64 -> Foo.
use std::convert::{TryFrom, TryInto};
struct Foo {
a: f64
}
impl TryFrom<&f64> for Foo {
type Error = String;
fn try_from(value: &f64) -> Result<Foo, String> {
Ok(Foo {
a: *value
})
}
}
fn main(){
let foo: Foo = 5.0.try_into().unwrap();
let bar: Foo = (&5.0).try_into().unwrap();
}
(Yes of course this is a pointless and stupid example, but it helps simplify the problem)
Now, the second line in the main method, with manual borrowing, succeeds.
However, the first line in the main method, without the manual borrowing, fails with this error:
error[E0277]: the trait bound `Foo: From<{float}>` is not satisfied
--> src/main.rs:18:24
|
18 | let foo: Foo = 5.0.try_into().unwrap();
| ^^^^^^^^ the trait `From<{float}>` is not implemented for `Foo`
|
= note: required because of the requirements on the impl of `Into<Foo>` for `{float}`
note: required because of the requirements on the impl of `TryFrom<{float}>` for `Foo`
--> src/main.rs:7:6
|
7 | impl TryFrom<&f64> for Foo {
| ^^^^^^^^^^^^^ ^^^
= note: required because of the requirements on the impl of `TryInto<Foo>` for `{float}`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error
Playground
Why is automatic borrowing not working here?
Just as the error message suggests, the problem is the trait bound Foo: From<{float}> is not satisfied. When matching traits, Rust will not perform any coercion but probing the suitable method. This is actually documented in The Rustonomicon, reads
Note that we do not perform coercions when matching traits (except for receivers, see the next page). If there is an impl for some type U and T coerces to U, that does not constitute an implementation for T.
and the next page says
Suppose we have a function foo that has a receiver (a self, &self or &mut self parameter). If we call value.foo(), the compiler needs to determine what type Self is before it can call the correct implementation of the function. ... If it can't call this function (for example, if the function has the wrong type or a trait isn't implemented for Self), then the compiler tries to add in an automatic reference. This means that the compiler tries <&T>::foo(value) and <&mut T>::foo(value). This is called an "autoref" method call.
So when matching the trait bound, Rust compiler will try to auto ref/deref on the type only. In addition, the dot operator in rust is just a syntax sugar of fully qualified function call. Thus 5.0.try_into().unwrap(); will become f64::try_into(5.0).unwrap(); and since TryInto is not implemented for f64, Rust will try to auto reference it by calling &f64::try_into(5.0).unwrap();. Now the compiler can find a version of TryInto implemented for &f64, however the type of argument still doesn't match: try_into for &f64 requires &f64 as parameter type, while the current call provides f64, and Rust compiler cannot do any coercion on parameters when checking trait bound. Thus the trait bound still doesn't match (&f64 cannot take f64 argument) and the check failed. Thus you will see the error message.
In my attempt to make my code a little bit cleaner, I tried passing the closure directly by name to the map() to an iterator for a &[&str]. map will need to iterate over &&str which is understandable but my closure only takes &str.
Code example:
fn capitalize_first(word: &str) -> String {
word.chars().next().unwrap().to_uppercase().to_string()
}
fn main() {
let cap_cap = |word: &&str| -> String {
word.chars().next().unwrap().to_uppercase().to_string()
};
let a = ["we", "are", "testing"];
let b = &a; // this is where this becomes interesting.
b.iter().map(|&x| capitalize_first(x)); // Works
b.iter().map(|x| capitalize_first(x)); // Works
b.iter().map(cap_cap); // That's what the compiler expects.
b.iter().map(capitalize_first); // fails to compile!
}
Compiler error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:17:18
|
1 | fn capitalize_first(word: &str) -> String {
| ----------------------------------------- found signature of `for<'r> fn(&'r str) -> _`
...
16 | b.iter().map(capitalize_first); // fails to compile!
| ^^^^^^^^^^^^^^^^ expected signature of `fn(&&str) -> _`
A couple of questions:
Why does this work map(|x| capitalize_first(x)) while map(capitalize_first) doesn't? What sort of magic happens behind the scenes in the first case? In my view, those should be identical.
Is there a reason on why rust can't transparently convert between &&str and &str?
Is there a reason on why rust can't transparently convert between &&str and &str?
Rust does transparently convert from &&str to &str, you can see it in one of your working examples:
b.iter().map(|x| capitalize_first(x))
// ^ x is a &&str here ^ but seems to be a &str here
What sort of magic happens behind the scenes[?]
The "magic" that happens is due to type coercions, specifically Deref coercion. "Type coercions are implicit operations that change the type of a value".
Why does this work map(|x| capitalize_first(x)) while map(capitalize_first) doesn't?
What you've discovered is that while there is a coercion for &&str to &str, there is no coercion from fn(&&str) to fn(&str). What coercions are available is pretty restricted.
On top of that, there are only specific places where coercion can occur, but what makes the first case work is that, with the closure, a coercion site is available right where you call capitalize_first:
b.iter().map(|x| capitalize_first(x))
// ^ the compiler injects code here to do the coercion
// as if you had written capitalize_first(*x)
When you use the function directly, there is no such site where coercion can occur.
I have the following code
let hazard_predicate = predicate::function(|x: &String| {
if (x == "You got it right!" || x == "You got it wrong!") {
return true;
} else {
return false;
}
});
let mut cmd = Command::cargo_bin("rust-starter").expect("Calling binary failed");
cmd.arg("hazard").assert().stdout(hazard_predicate);
It doesn't compile. It complains that hazard_predicate doesn't implement a particular trait.
Here is the error message
error[E0277]: the trait bound
`predicates::function::FnPredicate<[closure#core/tests/test_cli.rs:31:48: 37:6], std::string::String>: assert_cmd::assert::IntoOutputPredicate<_>` is not satisfied
--> core/tests/test_cli.rs:39:32
|
39 | cmd.arg("hazard").assert().stdout(hazard_predicate);
| ^^^^^^ the trait `assert_cmd::assert::IntoOutputPredicate<_>` is not implemented for `predicates::function::FnPredicate<[closure#core/tests/test_cli.rs:31:48: 37:6], std::string::String>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
So How do I implement that trait for my predicate function?
Let's look at the documentation for the types and traits in question. Usually the required traits are implemented automatically on all the types where it is possible, and, in fact, if the type in question is not your own, the trait has to be implemented by the library. So, first of all we check the assert_cmd docs to see what types can be used here.
There are two implementations which can be of interest for us:
impl<P> IntoOutputPredicate<StrOutputPredicate<P>> for P
where
P: Predicate<str>
impl<P> IntoOutputPredicate<P> for P
where
P: Predicate<[u8]>
Let's see now, what is the Predicate. This ends in the predicates-core crate, so it seems that at least some of the items from the predicates crate (based on this core) will be possible to use.
Now, let's try the other way round - look through the docs for predicate::function:
pub fn function<F, T>(function: F) -> FnPredicate<F, T>
where
F: Fn(&T) -> bool,
T: ?Sized,
Well then, we've got the type FnPredicate mentioned in the error message, so what traits are implemented by it?
impl<F, T> Predicate<T> for FnPredicate<F, T>
where
F: Fn(&T) -> bool,
T: ?Sized,
Here it is! You've passed a closure taking &String, so the T in this definition is inferred to be String, i.e. the implemented trait is Predicate<String>.
Now, if you recall the first part, you'll see that there is no Predicate<String> there in implementations!
How to resolve this?
I see two possibilities, as for now:
You can use the second implementation and make your predicate take reference to a byte slice &[u8]. I can't test it with the library itself, since it isn't on the playground, but if I make this change just in closure, I immediately get the error:
error[E0277]: can't compare `[u8]` with `str`
--> src/lib.rs:3:15
|
3 | if (x == "You got it right!" || x == "You got it wrong!") {
| ^^ no implementation for `[u8] == str`
|
= help: the trait `std::cmp::PartialEq<str>` is not implemented for `[u8]`
= note: required because of the requirements on the impl of `std::cmp::PartialEq<&str>` for `&[u8]`
Fortunately, this is fairly easily fixed by changing string literals to byte strings (playground):
let _ = |x: &[u8]| {
x == b"You got it right!" || x == b"You got it wrong!"
};
Note that I also took advantage of Clippy hints to simplify the code in question (on playground it is under Tools button on the right side).
Now, if you pass this closure into predicate::function, all should work fine.
Another way is to use the first implementation - you can see that Predicate<str>, i.e. function predicate receiving &str, is also supported, although in a bit more complex way. But for now this doesn't seem to be a problem, since the trait is implemented anyway - that's just one internal layer of indirection, but this is not your problem (assert_cmd crate should handle this itself). This code, in particular, compiles well:
use assert_cmd::{assert::OutputAssertExt, cargo::CommandCargoExt};
use predicates::prelude::*;
use std::process::Command;
fn main() {
let hazard_predicate =
predicate::function(|x: &str| x == "You got it right!" || x == "You got it wrong!");
let mut cmd = Command::cargo_bin("rust-starter").expect("Calling binary failed");
cmd.arg("hazard").assert().stdout(hazard_predicate);
}
Side note
There is a long-standing question here describing why this is bad to have the functions require &String (or &Vec, or &Box - a reference to the owned container, it is). In short - you can replace &String by &str, and this will not be a restriction. Of course, library authors know that too and usually force you to have the most general way, where you have to work with the least indirection possible.
This question already has answers here:
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
(2 answers)
Closed 4 years ago.
I'm trying to build a very basic example of an asynchronous callback function in Rust:
extern crate tokio;
extern crate tokio_core;
extern crate tokio_io;
use std::error::Error;
use tokio::prelude::future::ok;
use tokio::prelude::Future;
fn async_op() -> impl Future<Item = i32, Error = Box<Error>> {
ok(12)
}
fn main() {
let fut = async_op().and_then(|result| {
println!("result: {:?}", result);
});
tokio::run(fut);
}
This always results in the compiler error:
error[E0106]: missing lifetime specifier
--> src/main.rs:9:54
|
9 | fn async_op() -> impl Future<Item = i32, Error = Box<Error>> {
| ^^^^^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
= help: consider giving it a 'static lifetime
Why is there a lifetime error in the first place? Why is it only for the Error but not for the Item?
I am also unsure about "help: consider giving it a 'static lifetime" ‒ AFAICT this would result in a lifetime of the return value over the entire program execution, which is definitely not what I would want in a more complex example.
Everything in Rust has a lifetime bound. If it contains references, it is the lifetime of the shortest-lived reference, otherwise it is 'static, which should be interpreted as “does not depend on anything that could have shorter lifetime”.
Therefore the lifetime of i32 is known. It is 'static, because it does not contain any references.
But Error, which is std::error::Error is a trait, and one which does not require any lifetime bound. That means you could implement it for a reference, or a type including a reference with a lifetime. And since the code is supposed to be valid for any substitution you might make anywhere downstream, the compiler insists that you give it a lifetime that is lower bound for usability of the return value.
Giving it 'static lifetime is sensible here. It does not mean the return value would be valid over the entire program execution, it only means the the return value is not limited to any shorter lifetime (which basically means that it does not depend on anything outside the Box except possibly static things) and will stay valid as long as something holds on to it.
I believe the correct syntax is:
fn async_op() -> impl Future<Item = i32, Error = Box<Error + 'static>>
Note that it is really only limiting the content of the Box. That's the only thing the compiler does not see into and is concerned it might cease to be valid at some point. And thus you promise it that the content of the Box won't become invalid until it drops the Box.