0

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?

Omar Abid
  • 15,753
  • 28
  • 77
  • 108
  • Try changing your function argument from `&String` to `&str`. I'm on mobile, so full answer will arrive a bit later, but this seems to be the solution. – Cerberus Apr 30 '19 at 01:47

1 Answers1

4

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:

  1. 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.

  1. 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.

Cerberus
  • 8,879
  • 1
  • 25
  • 40
  • Thank you. This resolved my issue. But you went further to explain how Traits work which, I think, on practical terms this answer is better than any of the books or tutorials I have went through. – Omar Abid Apr 30 '19 at 23:11
  • Glad to hear it! Please check the edit - I've tested (compiling, not running) the `&str` case and added it to the answer, since this seem to work too. – Cerberus May 01 '19 at 03:26