如何实现功能特征

时间:2019-04-29 21:56:05

标签: rust traits

我有以下代码

nullable

它不能编译。它抱怨azar_predicate没有实现特定的特征。

这是错误消息

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);

那么我该如何为我的谓词函数实现该特征?

1 个答案:

答案 0 :(得分:2)

让我们看一下有关类型和特征的文档。通常,所需的特征会在可能的所有类型上自动实现,实际上,如果所涉及的类型不是您自己的类型,则特征必须由库实现。因此,首先我们检查assert_cmd docs,以了解此处可以使用哪些类型。

我们可能会感兴趣两种实现方式:

impl<P> IntoOutputPredicate<StrOutputPredicate<P>> for P
where
    P: Predicate<str>
impl<P> IntoOutputPredicate<P> for P
where
    P: Predicate<[u8]>

现在看看what is the Predicate。结束于predicates-core板条箱,因此似乎可以使用predicates板条箱中的至少某些物品(基于此核心)。

现在,让我们尝试另一种方式-浏览docs for predicate::function

pub fn function<F, T>(function: F) -> FnPredicate<F, T> 
where
    F: Fn(&T) -> bool,
    T: ?Sized, 

那么,我们在错误消息中提到了类型FnPredicate,所以是what traits are implemented吗?

impl<F, T> Predicate<T> for FnPredicate<F, T>
where
    F: Fn(&T) -> bool,
    T: ?Sized, 

在这里!您已通过闭包接受&String,因此此定义中的T推断为String,即实现的特征是Predicate<String>

现在,如果您回想起第一部分,您会发现实现中没有Predicate<String>

该如何解决?

到目前为止,我看到了两种可能性:

  1. 您可以使用第二种实现,并使谓词引用字节片&[u8]。因为它不在操场上,所以我无法使用库本身对其进行测试,但是如果我只是在关闭时进行了更改,我会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]`

幸运的是,通过将字符串文字更改为字节字符串(playground)可以很容易地解决此问题:

let _ = |x: &[u8]| {
    x == b"You got it right!" || x == b"You got it wrong!"
};

请注意,我还利用了Clippy提示来简化相关代码(在操场上,它位于右侧的“工具”按钮下)。

现在,如果您将此闭包传递给predicate::function,那么一切都应该正常工作。

  1. 另一种方式是使用第一种实现-您可以看到Predicate<str>,即,支持谓词接收&str的功能,尽管以一种更为复杂的方式。但是现在这似乎不是问题,因为无论如何都实现了特征-这只是间接的一个内部层,但这不是您的问题(assert_cmd板条箱应自行处理)。这段代码尤其可以很好地编译:
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);
}

旁注

a long-standing question here描述了为什么让功能需要&String(或&Vec&Box-对拥有的容器的引用,这很不好)的原因。简而言之-您可以将&String替换为&str,这不是限制。当然,图书馆作者也知道这一点,并且通常会迫使您采用最通用的方式,即您必须使用最少的间接方式进行工作。