我正在尝试将生锈中的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,但是从那时起生锈已经变得足够大,所以它不再有效。
答案 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));
}