如何实现函数的特征

问题描述 投票:-1回答:1

我有以下代码

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

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

这是错误消息

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

那么如何为谓词函数实现该特性呢?

rust traits
1个回答
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>

How to resolve this?

我看到两种可能性,就像现在一样:

  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 crate应该自己处理)。特别是这段代码编译得很好:
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

a long-standing question here描述为什么这些函数需要&String(或&Vec,或&Box - 对所拥有的容器的引用,这是不好的)。简而言之 - 你可以用&String替换&str,这不是限制。当然,图书馆作者也知道这一点并且通常会强迫您采用最通用的方式,在这种方式中您必须使用最少的间接方式。

© www.soinside.com 2019 - 2024. All rights reserved.