イヌツムリのメモ

学習したことのメモである。しかし、他の人が読んでもわかるように書こう。

Rustで高階関数を使う

はじめに

高階関数は便利だ。
なので、rustでも使いたい。
よって、使い方を検証した。

下記にrust環境構築方法をまとめている。

dockerとvscodeでrust環境を構築する on Windows10 - イヌツムリのメモ

結論

引数になる関数をBoxでラップすると、関数を引数に取れる。
戻り値になる関数をimplを指定すると関数を戻り値に取れる。

dynのありなしで何が変わるのか調べる。 -> traitオブジェクトを引数に取るときはdynをつける。
(2021/04/07 追記)

関数を引数にとるサンプルコード

fn map(f: Box<dyn Fn(i32) -> i32>, a: &Vec<i32>) -> Vec<i32>{
    let mut b = vec![];
    for &num in a{
        b.push(f(num));
    }
    b
}

fn twice(x: i32) -> i32{
    x + x
}

fn main() {
    let arry = &vec![4, 5, 3, 2];
    let v4 = map(Box::new(twice),arry);
    println!("{:?}", v4);
}

引数に渡すときはBox::new(関数名)で渡せる。
受け取り側の関数定義はBox<dyn Fn(i32) -> i32>で受け取れる。
(2021/04/07 追記)
dynをつけないと下記エラーでコンパイラに怒られる。
trait objects without an explicitdynare deprecated
トレイトを引数にとるのでなく、トレイトオブジェクトを引数に取るので、ということらしい。
トレイトではどんな関数が実装されているか不明で、メモリ確保ができないから、と理解した。
オブジェクト化されていれば、内容が明確なのでメモリ確保が可能になる。

関数を返すサンプルコード

fn map(f: Box<dyn Fn(i32) -> i32>, a: &Vec<i32>) -> Vec<i32>{
    let mut b = vec![];
    for &num in a{
        b.push(f(num));
    }
    b
}

fn add_x(x: i32) -> impl Fn(i32) -> i32{
    move |a: i32| -> i32{
        a + x
    }
}

fn main() {
    let arry2 = &vec![4, 5, 3, 2];
    let f = add_x(4);
    let v2 = map(Box::new(f),arry2);
    println!("{:?}", v2);
}

関数を戻り値に指定するにはimplを指定する。
関数を変数に束縛する際に引数を与える場合、moveしないと与えた変数の所有権を移せない。

Boxよりもimplを使ったほうがよい?

(2021/04/07 追記)
dynはトレイトオブジェクトを返すときに明示するとわかった。
なので、add_xの戻り値は、implでなく下記のようにBoxを用いて記述することもできる。

fn add_x(x: i32) -> Box<dyn Fn(i32) -> i32>{
    let f = move |a: i32| -> i32{
        a + x
    };
    Box::new(f)
}

fn main(){
    let  arry2 = &vec![4, 5, 3, 2];
    let f = add_x(5);
    let v2 = map(f,arry2);
    println!("{:?}", v2);
}

というか、下記のようにimpl使ったほうがスッキリとかける。
(i32をTに変えてるのは気にしないで...)
map()の宣言時や、mapの引数にtwiceを与えるときにBoxを使わなくて良いのでスッキリする。
ただ、dynがあとからrustに実装されたことを考えると、私の理解に間違いがあるように思う。

fn map<T>(f: impl Fn(T) -> T, a: &Vec<T>) -> Vec<T>
where T: Copy + std::ops::Add<Output = T> + std::ops::Sub<Output = T> + std::ops::Mul<Output = T> + std::ops::Div<Output = T>
{

    let mut b = vec![];
        for &num in a{
            b.push(f(num));
        }
        b
   
}

fn twice(x: i32) -> i32{
    x + x
}

fn main(){
    let arry1 = &vec![4, 5, 3, 2];
    let v4 = map(twice, &arry1);
    println!("{:?}", v4);
}