Rust Shr操作员

时间:2014-07-02 03:46:56

标签: rust

我正在尝试将生锈中的right shift operator>>)重载为implement a Maybe bind

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上找到了一个similar question,但是从那时起生锈已经变得足够大,所以它不再有效。

3 个答案:

答案 0 :(得分:5)

由于它对你造成的各种限制,重载右移操作符并不是一个好主意。它通过引用获取所有内容,而您想要的是通过 value 获取所有内容。

无法通过不可变引用调用闭包;你必须有一个可变的引用来调用它,因为它可能会改变它的环境。

您提到的问题时的解决方案是使用&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))

答案 1 :(得分: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)
}

答案 2 :(得分:3)

放弃运算符重载并稍微修改了一下生锈的宏后,我想出了如何实现一些很好的语法糖来链接Option map和绑定。代码可以在this 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));
}