rust - 如何为函数实现特征

标签 rust traits

我有以下代码

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

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

最佳答案

让我们看一下相关类型和特征的文档。通常所需的特征会在可能的所有类型上自动实现,事实上,如果所讨论的类型不是您自己的类型,则特征必须由库实现。所以,首先我们检查 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 crate ,所以看起来至少有一些来自 predicates 的元素crate(基于这个核心)将可以使用。

现在,让我们尝试反过来 - 查看 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. 您可以使用第二个实现并使您的谓词引用一个 byte slice &[u8] .我无法使用库本身对其进行测试,因为它不在 Playground 上,但如果我只是在关闭时进行此更改,我 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简化相关代码的提示(在 Playground 上,它位于右侧的“工具”按钮下)。

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

  1. 另一种方法是使用第一个实现 - 你可以看到 Predicate<str> ,即函数谓词接收 &str , 也受支持,尽管方式有点复杂。但现在这似乎不是问题,因为 trait 无论如何都实现了——这只是一个内部间接层,但这不是你的问题(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);
}

旁注

a long-standing question here描述为什么需要这些功能是不好的 &String (或 &Vec&Box - 对拥有的容器的引用,它是)。简而言之 - 您可以替换 &String通过 &str ,这将不是限制。当然,库作者也知道这一点,并且通常会强制您采用最通用的方式,您必须以尽可能少的间接方式工作。

关于rust - 如何为函数实现特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55911322/

相关文章:

random - 如何通过从字母数字字符中采样来创建随机字符串?

string - 多行字符串文字的语法是什么?

generics - 取决于特征的通用实现

php - 特征中的构造函数定义如何冲突?

php - 在哪里放置 Laravel 5 中的特征?

rust - 如何访问元组变量索引处的元素?

reference - 如何在引用而不是引用值上调用特征方法?

rust - Rust的确切自动引用规则是什么?

rust - Drop 不能用于实现扩展特征的通用结构

rust - 为什么将函数移至默认特征方法会导致借入错误?