简体   繁体   English

Rust Shr操作员

[英]Rust Shr Operator

I'm trying to overload the right shift operator ( >> ) in rust to implement a Maybe bind . 我试图在生锈中重载右移位运算符>> )以实现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() {}

However, I'm running into an error trying to invoke the closure: 但是,我在尝试调用闭包时遇到错误:

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

Why is it erroneous to invoke a borrowed closure, and how can I resolve the issue and implement the bind? 为什么调用借用的闭包是错误的,如何解决问题并实现绑定?

I found a similar question on stackoverflow, but rust has changed enough since then so that it no longer works. 我在stackoverflow上发现了一个类似的问题 ,但是从那以后生锈已经发生了很大变化,所以它不再有效。

Overloading the shift-right operator is not a good idea here because of various limitations it imposes on you. 由于它对您施加的各种限制,在此处重载右移操作符并不是一个好主意。 It takes everything by reference, whereas what you want is to take everything by value . 它通过引用获取所有内容,而您想要的是按获取所有内容。

It is not possible to call a closure through an immutable reference; 无法通过不可变引用调用闭包; you must have a mutable reference to call it, because it may mutate its environment. 你必须有一个可变引用来调用它,因为它可能会改变它的环境。

The solution in the time of the question you referred to was to use &fn(&A) -> B , which was an immutable closure; 你提到的问题时的解决方案是使用&fn(&A) -> B ,这是一个不可变的闭包; at present we do not have that type; 目前我们没有这种类型; |&A| -> B |&A| -> B is parallel to &mut fn(&A) -> B from that time, which simply can't work because it is being done through an immutable reference. |&A| -> B &mut fn(&A) -> B那个时间的&mut fn(&A) -> B平行,这根本无法工作,因为它是通过不可变引用完成的。 The sensible thing, of course, is to take self by value and have the function as |A| -> B 当然,明智的做法是通过价值取得self ,并具有|A| -> B的功能 |A| -> B ; |A| -> B ; this is what Option.and_then , which is exactly what you are trying to implement, does. 这就是Option.and_then ,这正是你想要实现的东西。

In short, what you are trying to do is currently impossible, though it might become possible again at some point in the future. 简而言之,你想要做的事情目前是不可能的,虽然它可能在未来的某个时候再次成为可能。 Use a regular method instead of trying to overload the operator. 使用常规方法而不是尝试重载运算符。

Or just use Option , already there: 或者只使用Option ,已经存在:

Some(1i).and_then(|x| Some(1 + x))
        .and_then(|x| Some(x * 2))

This is possible nowadays. 现在这是可能的。 Shr takes by value, and there are unboxed closures: 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)
}

After giving up on operator overloading and after tinkering with rust's macros a bit, I figured out how to implement some nice syntactic sugar for chaining Option maps and binds. 在放弃运算符重载并稍微修改一下Rust的宏之后,我想出了如何实现一些很好的语法糖来链接Option map和绑定。 The code can be found in this gist and is included here for convenience: 代码可以在这个要点中找到,为方便起见包括在这里:

#![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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM