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.
Related
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.
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.
This snippet is valid in Rust 1.26.1:
use std::ops::AddAssign;
trait Trait
where
for<'a> Self: AddAssign<Self> + AddAssign<&'a Self> + Sized,
{
}
trait Trait2 {
type Associated: Trait;
fn method(u32) -> Self::Associated;
}
fn func<T2: Trait2>() {
let mut t = T2::method(1);
let t2 = T2::method(2);
t += &t2;
}
Notice that Trait implements both AddAssign<Self> and AddAssign<&'a Trait> (in that order, which is important later). Therefore, in func we know that both t += t2 and t += &t2 should be valid. As seen on the playground, t += &t2 is valid, but using t += t2 isn't:
error[E0308]: mismatched types
--> src/main.rs:19:10
|
19 | t += t2;
| ^^
| |
| expected reference, found associated type
| help: consider borrowing here: `&t2`
|
= note: expected type `&<T2 as Trait2>::Associated`
found type `<T2 as Trait2>::Associated`
I read this error as the compiler not recognizing that AddAssign<Self> is implemented for T::Associated, which is clearly wrong, as it implements Trait, which requires AddAssign<Self>.
If we change the order of the AddAssign bounds on Trait then the opposite holds: t += t2 is valid while t += &t2 isn't.
A quick fix for the problem is to make func generic over both traits:
fn func<T: Trait, T2: Trait2<Associated = T>>() {
let mut t = T2::method(1);
let t2 = T2::method(2);
t += t2;
}
This shouldn't be necessary; the compiler can recognize one of the AddAssigns, why not the other? It seems the last bound is the one to be recognized.
My first suspicion was this this has something to do with dynamic dispatch.
I ruled it out since the order of the bounds shouldn't matter even in dynamic dispatch. I don't even think it uses it, since all types are known at compile-time using monomorphisation.
My current suspicion is a compiler bug where the typechecker doesn't account for generics on trait bounds when it is an associated type. It is easy to imagine such a specific case being overlooked.
What is going here?
This is a known bug (or a combination of a few):
Higher-ranked trait bounds on associated types are not elaborated (#50346).
where clauses are only elaborated for supertraits, and not other things (#20671)
Constraints on associated types declared in subtraits do not propagate. (#32722)
Unrecognized associated type bound on another associated type (#24159)
The workaround is to restate the bounds at every usage site:
fn func<T2>()
where
T: Trait2,
T::Associated: Trait,
{
let mut t = T::method(1);
let t2 = T::method(2);
t += &t2;
t += t2;
}
This should be addressed when the type system moves from its ad hoc implementation to Chalk, a more principled solver for the types of problems a complicated type system creates.
I have managed to make the Rust type checker go into an infinite loop. A very similar program compiles with no trouble. Why does the program I want not compile?
To save your time and effort, I have made minimal versions of the two programs that isolate the problem. Of course, the minimal version is a pointless program. You'll have to use your imagination to see my motivation.
Success
Let me start with the version that works. The struct F<T> wraps a T. The type Target can be converted from an F<T> provided T can.
struct F<T>(T);
impl<T> From<F<T>> for Target where Target: From<T> {
fn from(a: F<T>) -> Target {
let b = Target::from(a.0);
f(&b)
}
}
Here's an example caller:
fn main() {
let x = Target;
let y = F(F(F(x)));
let z = Target::from(y);
println!("{:?}", z);
}
This runs and prints "Target".
Failure
The function f does not consume its argument. I would prefer it if the From conversion also did not consume its argument, because the type F<T> could be expensive or impossible to clone. I can write a custom trait FromRef that differs from std::convert::From by accepting an immutable borrow instead of an owned value:
trait FromRef<T> {
fn from_ref(a: &T) -> Self;
}
Of course, I ultimately want to use From<&'a T>, but by defining my own trait I can ask my question more clearly, without messing around with lifetime parameters. (The behaviour of the type-checker is the same using From<&'a T>).
Here's my implementation:
impl<T> FromRef<F<T>> for Target where Target: FromRef<T> {
fn from_ref(a: &F<T>) -> Target {
let b = Target::from_ref(&a.0);
f(&b)
}
}
This compiles. However, the main() function doesn't:
fn main() {
let x = Target;
let y = F(F(F(x)));
let z = Target::from_ref(y);
println!("{:?}", z);
}
It gives a huge error message beginning:
error[E0275]: overflow evaluating the requirement `_: std::marker::Sized`
--> <anon>:26:13
|
26 | let z = Target::from_ref(y);
| ^^^^^^^^^^^^^^^^
|
= note: consider adding a `#![recursion_limit="128"]` attribute to your crate
= note: required because of the requirements on the impl of `FromRef<F<_>>` for `Target`
= note: required because of the requirements on the impl of `FromRef<F<F<_>>>` for `Target`
= note: required because of the requirements on the impl of `FromRef<F<F<F<_>>>>` for `Target`
etc...
What am I doing wrong?
Update
I've randomly fixed it!
The problem was that I forgot to implement FromRef<Target> for Target.
So I would now like to know: what was the compiler thinking? I still can't relate the problem to the error message.
You can't avoid consuming the input in the standard From/Into traits.
They are defined to always consume the input. Their definition specifies both input and output as owned types, with unrelated lifetimes, so you can't even "cheat" by trying to consume a reference.
If you're returning a reference, you can implement AsRef<T> instead. Or if your type is a thin wrapper/smart pointer, Deref<T>. You can provide methods as_foo()
If you're returning a new (owned) object, the convention is to provide to_foo() methods.