

Rustを勉強中の初心者です。  
構造体のメソッドを受け取る関数を作成したいのですが、うまく関数に渡せないので、ご助言をお願いします。

**試したこと**  
「関数を受け取る関数 - Rust by Example」  
<http://doc.rust-jp.rs/rust-by-example-ja/fn/closures/input_functions.html>  
を参考にして、下記のコードを作成しました。

[code]

    struct Foo {
      s: String,
    }
    impl Foo {
      fn method(&self, s: &String) {
        println!("{}", s);
      }
    }
    
    fn function(s: &String) {
      println!("{}", s);
    }
    
    fn execute<F>(f: F, s: &String)
    where
      F: Fn(&String),
    {
      f(s);
    }
    
    fn main() {
      let s = String::from("function!");
      execute(function, &s);
      // >>> function!
    
      let s = String::from("method!");
      let foo = Foo { s };
      execute(foo.method, &foo.s);
      // >>> Error
    }
    
[/code]

**結果**

`execute(function, &s);` は実行できていますが、 `execute(foo.method, &foo.s);`
の部分で下記のエラーが出ます。

[code]

    error[E0615]: attempted to take value of method `method` on type `Foo`
      --> src/main.rs:35:15
       |
    35 |   execute(foo.method, &foo.s);
       |               ^^^^^^ method, not a field
       |
    help: use parentheses to call the method
       |
    35 |   execute(foo.method(_), &foo.s);
       |                     +++
    
[/code]

しかしながら、やりたいことは `execute(foo.method(&foo.s));` のように「メソッドの返り値を関数に渡す」ことではありません。

構造体のメソッドを関数に渡すには、どのようにすれば良いのでしょうか？  
よろしくお願いします。


CC BY-SA 4.0
2021-12-11T11:47:22.087
84065
2021-12-11T15:18:31.427
49463
question
rust
Rustで、構造体のメソッドを受け取る関数はどのようにすれば作れますか？


クロージャを指定すれば、（記述量は増えますが）やりたいことは実現できます。

[code]

    execute(|x| foo.method(x), &foo.s);
    
[/code]

* * *

関数そのものを渡したいということならば、次のような記述もあります。  
ただ、そもそも `method` 関数は `self` と `s` の2つの引数を取る関数ですので、メソッドを受け取る関数に修正が必要です。

[code]

    fn execute2<F>(f: F, foo: &Foo, s: &String)
    where
      F: Fn(&Foo, &String),
    {
      f(foo, s);
    }
    
    execute2(Foo::method, &foo, &foo.s); // -> method!
    
[/code]


CC BY-SA 4.0
2021-12-11T14:56:30.120
84066
2021-12-11T14:56:30.120
20885
84065
answer


ご質問のコードでは、`execute(foo.method, &foo.s)`のところの他にもう1つ問題があります。順番に直していきましょう。

まず`execute`関数の引数`f: F`ですが、

[code]

        // 引数として &String を1つだけ取る
        F: Fn(&String),
    
[/code]

これは`Fn(&String)`型です。つまり引数を1つだけ取るクロージャーです。

ところが`Foo`の`method`メソッドは以下の定義になっており、引数を2つとります。

[code]

    impl Foo {
        // 引数として self: &Foo と s: &Stringの2つを取る
        fn method(&self, s: &String) {
    
[/code]

`method`は`Fn(&Foo, &String)`型になるので、`execute`関数の引数`f`（`Fn(&String)`型）には渡せません。

まずはこれを修正しましょう。

そもそも`Foo`は`s: String`というフィールドを持っていますので、`fn method(&self, s:
&String)`のように、もう1つの`s`を引数に取る必要はありません。以下のように変更します。

**変更前**

[code]

    impl Foo {
        // 引数として self: &Foo と s: &Stringの2つを取る
        fn method(&self, s: &String) {
            println!("{}", s);
        }
    }
    
[/code]

**変更後**

[code]

    impl Foo {
        // 引数として self: &Foo を1つだけ取る
        fn method(&self) {
            // フィールドsを使う
            println!("{}", self.s);
        }
    }
    
[/code]

これで`method`は引数を1つだけ取る`Fn(&Foo)`型になりました。

次に`execute`関数を修正します。第2引数`s: &String`を、任意の型`T`を受け付けるように変更します。

[code]

    fn execute<F, T>(f: F, t: &T)
    where
        F: Fn(&T),
    {
        f(t);
    }
    
[/code]

こうすることで`Fn(&Foo)`型の`method`と、`Fn(&String)`型の`function`の両方を引数に取れるようになります。

最後に元々エラーになっていた、以下のコードを修正しましょう。

[code]

    execute(foo.method, &foo.s);
    
[/code]

Rustのメソッドは関連関数の一種にすぎません。`foo`が`Foo`型のとき`foo.method()`と書くと、コンパイラーは`Foo::method(&foo)`と書かれたのだと解釈します。そのため以下のように書けます。

[code]

    // Fooのmethodメソッドを第1引数に、&Fooを第2引数にする
    execute(Foo::method, &foo);
    
[/code]

これでコンパイルできるようになります。実行結果は以下のとおりです。

[code]

    $ cargo run
    function!
    method!
    
[/code]

全体のコードは以下のようになります。

[code]

    struct Foo {
        s: String,
    }
    
    impl Foo {
        fn method(&self) {
            println!("{}", self.s);
        }
    }
    
    fn function(s: &String) {
        println!("{}", s);
    }
    
    fn execute<F, T>(f: F, t: &T)
    where
        F: Fn(&T),
    {
        f(t)
    }
    
    fn main() {
        let s = String::from("function!");
        execute(function, &s);
        // >>> function!
    
        let s = String::from("method!");
        let foo = Foo { s };
        execute(Foo::method, &foo);
        // >>> method!
    }
    
[/code]


CC BY-SA 4.0
2021-12-11T15:18:31.427
84067
2021-12-11T15:18:31.427
14101
84065
answer