Rust Shr 运算符

5

我正在尝试在 Rust 中重载 右移运算符 (>>),以 实现一个 Maybe 绑定

enum Maybe<T> {
    Nothing,
    Just(T)
}

/// Maybe bind. For example:
///
/// ```
/// Just(1) >> |x| Just(1 + x) >> |x| Just(x * 2)
/// ```
impl<'a, T, U> Shr<|&T|: 'a -> Maybe<U>, Maybe<U>> for Maybe<T> {
    fn shr(&self, f: &|&T| -> Maybe<U>) -> Maybe<U> {
        match *self {
            Nothing => Nothing,
            Just(ref x) => (*f)(x)
        }
    }
}

fn main() {}

然而,在尝试调用闭包时,我遇到了一个错误:
<anon>:15:28: 15:32 error: closure invocation in a `&` reference
<anon>:15             Just(ref x) => (*f)(x)
                                     ^~~~
error: aborting due to previous error
playpen: application terminated with error code 101
Program ended.

为什么调用借用闭包是错误的,如何解决这个问题并实现绑定?
我在stackoverflow上找到了一个类似的问题,但自那以后rust发生了足够的变化,以至于它不再起作用。

1
你是否知道这正是 Option.and_then 所做的(尽管它会消耗 self,因此能够通过值而不是引用传递对象,这显然更优)? - Chris Morgan
3个回答

5
重载右移运算符在这里不是一个好主意,因为它对你施加了各种限制。它将所有内容都作为引用传递,而你所想要的是按值传递。
无法通过不可变引用调用闭包;必须有一个可变引用来调用它,因为它可能会改变其环境。
问题时间的解决方案是使用`&fn(&A) -> B`,这是一个不可变的闭包;现在我们没有这种类型;`|&A| -> B`与那个时候的`&mut fn(&A) -> B`平行,但它不能工作,因为它是通过一个不可变引用完成的。当然,明智的做法是按值取`self`并将函数设为`|A| -> B`;这正是`Option.and_then`所做的,这正是你试图实现的。
简而言之,你试图做的目前是不可能的,但在未来某个时候可能会再次变得可能。使用常规方法而不是尝试重载运算符。
或者直接使用已经存在的`Option`。
Some(1i).and_then(|x| Some(1 + x))
        .and_then(|x| Some(x * 2))

谢谢!我有一种预感,重载运算符目前是不可能的,但只是想确认一下。我之前也没有见过 and_then。我一定会把它加入到我的工具库中。 - mwhittaker
值得一看的是浏览所有可用的Option方法 - 这是一个相当广泛的范围! - Chris Morgan

4

如今这是可能的。 Shr 按值传递,而且有未打包闭包:

use std::ops::Shr;
use Maybe::{Nothing,Just};

#[derive(Debug)]
enum Maybe<T> {
    Nothing,
    Just(T)
}

impl<T, U, F> Shr<F> for Maybe<T>
    where F: FnOnce(T) -> Maybe<U>
{
    type Output = Maybe<U>;

    fn shr(self, f: F) -> Maybe<U> {
        match self {
            Nothing => Nothing,
            Just(x) => f(x)
        }
    }
}

fn main() {
    let a = Just(1u8);
    let b = a >> |v: u8| Just(v + 1);
    println!("{:?}", b)
}

3

在放弃操作符重载并尝试使用Rust的宏之后,我找到了一种实现Option映射和绑定链式调用的简洁语法糖的方法。代码可以在这个Gist中找到,并为方便起见在此处包含:

#![feature(macro_rules)] 

macro_rules! map(
    ()                                    => ({});
    ($a:expr)                             => ($a);
    ($a:expr -> $b:expr)                  => ($a.map($b));
    ($a:expr -> $b:expr -> $($c:expr)->*) => (map!($a.map($b) -> $($c)->*));
)

macro_rules! flatbind(
    ()                                    => ({});
    ($a:expr)                             => ($a);
    ($a:expr -> $b:expr)                  => ($a.and_then($b));
    ($a:expr -> $b:expr -> $($c:expr)->*) => (flatbind!($a.and_then($b) -> $($c)->*));
)

macro_rules! bind(
    () => ({});
    ($a:expr) => ($a);
    ($a:expr -> |$var:ident| $body:expr) => ($a.and_then(|$var| $body));
    ($a:expr -> |$var:ident| $body:expr -> $(|$vars:ident| $bodies:expr)->*) => ($a.and_then(|$var| {bind!($body -> $(|$vars| $bodies)->*)}));
)

fn main() {
    // Equivalent rust code:
    // Some("12345")
    // .map(|s| s.to_string())
    // .map(|s| s.len())
    // .map(|l| l * l)
    let o = map!(
        Some("12345")     ->
        |s| s.to_string() ->
        |s| s.len()       ->
        |l| l * l
    );
    assert!(o == Some(25));

    // Equivalent rust code:
    // Some("12345")
    // .and_then(|s| Some(s.to_string()))
    // .and_then(|s| Some(s.len()))
    // .and_then(|l| Some(l * l))
    let o = flatbind!(
        Some("12345")           ->
        |s| Some(s.to_string()) ->
        |s| Some(s.len())       ->
        |l| Some(l * l)
    );
    assert!(o == Some(25));

    // Equivalent OCaml code:
    // Some 3 >>= fun x ->
    // Some 4 >>= fun y ->
    // Some 5 >>= fun z ->
    // Some(z*z - x*x - y*y)
    //
    // Equivalent rust code:
    // Some(3i).and_then( |x| {
    //     Some(4i).and_then |y| {
    //         Some(5i).and_then |z| {
    //             Some(z*z - x*x - y*y)
    //         }
    //     }
    // })
    let o = bind!(
        Some(3i) -> |x| 
        Some(4i) -> |y| 
        Some(5i) -> |z| {
            assert!(x == 3i);
            assert!(y == 4i);
            assert!(z == 5i);
            Some(z*z - x*x - y*y)
        }
    );
    assert!(o == Some(0));
}

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