Scala
Rust

RustでEitherを実装する(している)

きっかけ

最近、Rustを始めて標準でEitherがないのは不便と思ったので、実装しています。
元々、Scalaをやっていたので実装はScalaに合わせています。

困っていること

flatMapの実装

ScalaだとasInstanceOfを使っていて以下のような型変換が可能ですが、

import scala.util.Right

val r = Right("right") // scala.util.Right[Nothing,String]
r.asInstanceOf[Either[Nothing, Int]] // Either[Nothing,Int]

Rustだとこんな便利関数は見つけられませんでした。
そこで、これを再現する方法を以下の2つ思いつきましたが、結局transmuteを使う事で実現したい事の半分は出来ました。
(ジェネリクス使った際のキャストのベストプラクティスわかる方いたら教えて欲しいです)

  • transmute
  • libc::mallocで頑張る

transmuteはコンパイル時にサイズがわかっていないといけない(?)のでBoxに包んで誤魔化す事でコンパイルは出来ました。

問題発生

コンパイルは通り、実行してみると落ちるという現象が起きました。
落ちる原因は型のサイズ違いによりメモリ違反?かなんかが起きていると考えています。
(バイナリのデバック方法をよくわかっていない)
実行時に落ちるのは使い物にならないので、これを何とかしたいけど現状方法が何も思いついていません。
以下、再現コードです。

extern crate scala;

use std::num::ParseIntError;
use scala::util::Either;

fn main() {
    let result = f("a1000")
        .flat_map(Box::new(|n|f1(n)));
    println!("{:?}", result); //Left(ParseIntError { kind: InvalidDigit })

    let result = f("a1000")
        .flat_map(Box::new(|n|e1(n)));
    //printlnしたら落ちる
    //println!("{:?}", result);
}

fn f(s: &str) -> Either<ParseIntError, u64> {
    Either::parse_result(s.parse())
}

fn f1(n: u64) -> Either<ParseIntError, u8> {
    Either::parse_result(format!("{}", n).parse())
}

fn e1(n: u64) -> Either<String, String> {
    if n == 1000 {
        Either::Right("n is 1000".to_string())
    } else {
        Either::Left("n is not 1000".to_string())
    }
}
# cargo run
   Compiling scala v0.1.0 (file:///root/scala)
    Finished dev [unoptimized + debuginfo] target(s) in 1.25 secs
     Running `target/debug/scala`
Left(ParseIntError { kind: InvalidDigit })
Illegal instruction (core dumped)

今後実装したい事

for式

簡単にですが、以下のように使えるように実装してみました。
e_forという名前なのはforが使えなかったからです。
変数束縛とかちゃんと実装していきたいです。

let result = e_for!{
    _ <- f("1100")
    _ <- f1()
    _ <- f2()
};

他のライブラリ

Scalaの標準ライブラリは書きやすいのが多いので実装していきたい。