RustでIteratorやクロージャーなどのtraitを返す関数を作ろうとするとハマります。
Rustの関数は帰り値はスタック上のサイズを知る必要があります。
つまりRustの関数は具象型しか返せません。 traitとか返せないわけです。
どうしてもtraitを関数から返したい場合はBoxなどに包んでやる必要があります
特にクロージャーtraitは具象型をユーザーが書くのは不可能なのでクロージャー返したい場合はどうしてもBoxで包む必要がありました
fn count(n: u32) -> Box<FnMut() -> u32> {
let mut num = n;
Box::new(move || {num += 1; num})
}
fn main() {
let mut counter = count(0);
println!("{}", counter()); //1
println!("{}", counter()); //2
println!("{}", counter()); //3
}
traitを返したい場合にBox::newによるアロケートと
返されたtraitのメソッドを使う場合に動的ディスパッチが行われるのは残念です。
あとオブジェクト安全なtraitしかBoxに入れる事はできません。
オブジェクト安全ではないtraitを関数から返すのは不可能です。 ファ○ク
##impl trait
Nightlyコンパイラの場合#![feature(conservative_impl_trait)]
を指定するとimpl traitを使えます。
返したいtraitの前にimplをつけるとtraitを関数の戻り値の型として指定できます。
実行時の動きとしては具象型を指定した場合と変わらないはずです。
オブジェクト安全である事も要求されません。
//例
fn hoge() -> impl Fuge { .. }
最初の例をimpl traitで書き直してみましょう
#![feature(conservative_impl_trait)]
fn count(n: u32) -> impl FnMut() -> u32 {
let mut num = n;
move || {num += 1; num}
}
fn main() {
let mut counter = count(0);
println!("{}", counter()); //1
println!("{}", counter()); //2
println!("{}", counter()); //3
}
Box使わずにクロージャを返せました
他にもiteratorを関数から返したい時なんかにも便利です。
impl traitが使えない場合
- 返す型が違う場合
スタック上のサイズが違う可能性があるので当たり前ですね。
諦めてBoxに包みましょう。
- trait内のメソッド
これは何故でしょうか。
あくまで関数の戻り値の型専用ってことでしょうか。
誰か分かる人教えて下さい。
参考
http://www.ncameron.org/blog/abstract-return-types-aka-%60impl-trait%60/
https://doc.rust-lang.org/beta/book/trait-objects.html