きっかけ
最近、Rustを始めて標準でEitherがないのは不便と思ったので、実装しています。
元々、Scalaをやっていたので実装はScalaに合わせています。
困っていること
flatMapの実装
- 今回の実装
- Scalaの実装
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の標準ライブラリは書きやすいのが多いので実装していきたい。