Rust中的组合运算符和管道前向运算符

4个回答

27

虽然没有内置此运算符,但定义它并不特别困难:

use std::ops::Shr;

struct Wrapped<T>(T);

impl<A, B, F> Shr<F> for Wrapped<A>
where
    F: FnOnce(A) -> B,
{
    type Output = Wrapped<B>;

    fn shr(self, f: F) -> Wrapped<B> {
        Wrapped(f(self.0))
    }
}

fn main() {
    let string = Wrapped(1) >> (|x| x + 1) >> (|x| 2 * x) >> (|x: i32| x.to_string());
    println!("{}", string.0);
}
// prints `4`
Wrapped 结构体是为了让 Shr 实例可以使用,否则我们需要在一个通用的结构体上实现它(比如 impl<A, B> Shr<...> for A),但这样做是行不通的。
需要注意的是,惯用的 Rust 语言会使用方法 map 来代替操作符。请参考 Option::map 的规范示例。

每个步骤周围的括号是必需的吗?我不太确定操作顺序是什么。什么是“Shr”?这是您如何重载“shift right”运算符吗?我们可以定义另一个吗?比如 |> - mpen
1
@dbaupp 这让我有点难过。语法不太像我想要的那样好。 - mpen
2
这个不再编译了。“self” 不再是一个特殊的生命周期。有没有一种方法可以在当前的 Rust 中实现它? - diogovk
1
我添加了一个答案,其中包含与1.16.0编译的版本。http://stackoverflow.com/a/43163837/545475 - Arnavion
1
(现已合并到此答案中。) - Arnavion
显示剩余6条评论

4
据我所知,Rust中不存在这些运算符。目前为止,我并没有感到需要它们的必要性,同时也有努力使核心Rust语法保持相对简单的工作。例如,Rust曾经使用显式消息传递运算符,但已被移除。
如果您想要类似的功能,可能可以使用运算符重载来实现,或者编写自己的组合或管道前向函数。我不会感到惊讶,如果Rust团队愿意将这些包含在标准库中。

3

虽然这些运算符本身并不存在,Option::map 提供了一个惯用的类比。

fn do_some_stuff_to_a_number(x: Option<i32>) -> Option<String> {
    x.map(|x| x + 1).map(|x| 2 * x).map(|x: i32| x.to_string())
}

如果没有函数:

let string = Some(1)
    .map(|x| x + 1)
    .map(|x| 2 * x)
    .map(|x: i32| x.to_string())?;

这个问题已经被现有答案解决了:请注意,惯用的 Rust 会将此方法称为 map 而不是使用运算符。请参阅 Option :: map 以获取规范示例。 - Shepmaster
2
@Shepmaster 谢谢你,但是目前的回答都没有提供实际的惯用例子。我知道目前被接受的答案_提到了_它,但是没有提供任何额外的信息,并且还提供了一个实现反模式的例子。 - Brandon Dyer
3
这应该是被接受的答案,除非现在惯用的 Rust 是在 Rust 中编写不惯用的黑客技巧,以便您可以在 Rust 中编写惯用的 Haskell。 - user11877195

1
实际上,Rust有一个相似的运算符,称为点运算符,但它不能与独立函数一起使用,这是唯一的主要问题。即未在impl内定义的函数!您可以编写像Option :: map(Option :: Some(1),| x | x + 1)这样的代码,它是'f(g(x))'样式或使用点的代码,例如Option :: Some(1).map(| x | x + 1),这更类似于'f | > g(x)'样式。点运算符允许您随意链接方法,但为了使编译器工作更轻松,还有一些限制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接