2014-07-02 8 views
5

Ich versuche, die right shift operator (>>) in Rost zu implement a Maybe bind zu überlasten.Rost Shr Betreiber

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() {} 

aber ich laufe in einen Fehler versucht, die Schließung aufzurufen:

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

Warum ist es falsch einem geliehenen Verschluss zu berufen, und wie kann ich das Problem beheben und implementieren die bind ?

Ich habe eine similar question auf Stackoverflow gefunden, aber Rost hat sich seit damals genug geändert, so dass es nicht mehr funktioniert.

+1

Sie wissen, dass dies genau das, was 'Option.and_then' tut (obwohl es' self' verbraucht und so in der Lage, das Objekt nach Wert zu übergeben, anstatt durch Bezugnahme, die deutlich überlegen ist)? –

Antwort

5

Das Überladen des Shift-Right-Operators ist hier wegen der verschiedenen Einschränkungen, die Ihnen auferlegt werden, keine gute Idee. Es nimmt alles durch Bezugnahme, während Sie wollen, ist alles durch Wert zu nehmen.

Es ist nicht möglich, eine Schließung durch eine unveränderliche Referenz aufzurufen; Sie müssen eine veränderbare Referenz haben, um sie aufzurufen, weil sie ihre Umgebung mutieren kann.

Die Lösung in der Zeit der Frage, auf die Sie sich bezogen, war die Verwendung &fn(&A) -> B, die eine unveränderliche Schließung war; Gegenwärtig haben wir diesen Typ nicht; |&A| -> B ist parallel zu &mut fn(&A) -> B von dieser Zeit, die einfach nicht funktionieren kann, weil es durch eine unveränderliche Referenz getan wird. Die sinnvolle Sache ist natürlich, self wertmäßig zu nehmen und die Funktion als |A| -> B zu haben; das ist was Option.and_then, das ist genau was Sie versuchen zu implementieren, tut.

Kurz gesagt, was Sie versuchen zu tun, ist derzeit unmöglich, obwohl es irgendwann in der Zukunft wieder möglich sein könnte. Verwenden Sie eine normale Methode, anstatt zu versuchen, den Operator zu überlasten.

Oder benutzen Sie einfach Option, schon da:

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

Vielen Dank! Ich hatte eine Ahnung, dass das Überladen des Betreibers zur Zeit unmöglich war, wollte aber nur bestätigen. Ich hatte auch vorher noch nie 'and_then' gesehen. Ich werde es definitiv zu meinem Arsenal hinzufügen. – mwhittaker

+0

Es lohnt sich, sich die verschiedenen Methoden anzusehen, die für "Option" verfügbar sind - es ist eine ziemlich große Auswahl! –

3

Nach dem Einschalten Betreiber Überlastung Aufgeben und nach mit Rost des Makros ein bisschen Bastelei, ich herausgefunden, wie für die Verkettung Option Karten und bindet einige schöne syntaktischer Zucker zu implementieren . Der Code kann in this gist gefunden werden und ist hier der Einfachheit halber enthalten:

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

Dies ist heute möglich. Shr nimmt nach Wert, und es gibt unboxed Verschlüsse:

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) 
}