82332


先日、以下のRustコードの2行目の `.map(Some)` のように、`map`の引数にクロージャではなく `Some`や
`Ok`を渡しているのを見かけました。

[code]

    let v = vec![1,2,3];
    let a = v.iter().map(Some).collect::<Vec<_>>();
    println!("{:?}", a); //=> [Some(1), Some(2), Some(3)]
    
[/code]

これの出力が`[Some(1), Some(2), Some(3)]`
となるのは、変数に束縛したクロージャを渡すのと同じ見た目なので、直観的には理解できます。

[code]

    let v = vec![1,2,3];
    let fun = |x| x + 3;
    let v = v.iter().map(fun).collect::<Vec<_>>();
    println!("{:?}", a); //=> [4, 5, 6]
    
[/code]

公式リファレンスの[`map`](https://doc.rust-
lang.org/1.54.0/std/iter/trait.Iterator.html#method.map)には次の記述があります。しかし、リファレンスを見る限り`Option`は`FnMut`を実装していないようです。

> `map()` transforms one iterator into another, by means of its argument:
> something that implements `FnMut`.

いくら`fun(x)` と`Some(x)`で書き方が似ていようと、前者はクロージャ、後者はenumです。どうして上記の書き方でうまくいくのでしょうか？

なお、Rubyでは `Symbol#to_proc` を使って、メソッド名からブロック（クロージャ）を生成できます。オブジェクトを文字列に変換する
`to_s` メソッドにこれを使うと、 `[1, 2, 3].map(&:to_s) == [1, 2, 3].map {|x| x.to_s} ==
["1", "2", "3"]` と書けます。これと似た仕組みがあるのでしょうか？


CC BY-SA 4.0
2021-09-08T02:41:46.003
82330
2021-09-08T05:41:12.920
7500
question
rust
クロージャを渡す関数にenumの名前のみを渡す


現時点では私も完全な答えがわからないので、わかるところについて回答します。（後でもし何か分かったら追記します → 追記しました）

> なお、Rubyでは `Symbol#to_proc` を使って、メソッド名からブロック（クロージャ）を生成できます。オブジェクトを文字列に変換する
> `to_s` メソッドにこれを使うと、 `[1, 2, 3].map(&:to_s) == [1, 2, 3].map {|x| x.to_s} ==
> ["1", "2", "3"]` と書けます。これと似た仕組みがあるのでしょうか？

はい。これに似た仕組みはあります。Rustではsafeな関数ポインターは `Fn`, `FnMut`, `FnOnce` を実装します。

<https://doc.rust-lang.org/stable/std/primitive.fn.html>

> all _safe_ function pointers implement `Fn`, `FnMut`, and `FnOnce`.

これを使うと、以下のようなコードを書けます。

[code]

    // Some(x)を返すsafeな関数
    fn make_some<T>(x: T) -> Option<T> {
        Some(x)
    }
    
    let v = vec![1,2,3];
    // mapが要求している `FnMut(Self::Item) -> B` のところに `make_some` を
    // 渡すことができる。なぜなら `make_some` は `FnMut(T) -> Option<T>` を
    // 実装しており、要件を満たしているから。
    let a = v.iter().map(make_some).collect::<Vec<_>>();
    println!("{:?}", a); //=> [Some(1), Some(2), Some(3)]
    
[/code]

> いくら`fun(x)` と`Some(x)`で書き方が似ていようと、前者はクロージャ、後者はenumです。どうして上記の書き方でうまくいくのでしょうか？

こちらについてはどういう仕組みなのか私にもわかりません。たぶん`Some(x)`を`make_some(x)`関数のように扱っているのだと思いますが、それを裏付ける情報が見つかりません。何か分かったら追記します。

**追記**

Slackの日本語Rustコミュニティー（rust-jp）で、裏付けとなる情報があることを[教えてもらい](https://rust-
jp.slack.com/archives/C0DJCNRPC/p1631072212046600)ました（Slack rust-
jpの[入会ページ](https://rust-jp.herokuapp.com/)）

  * 公式ガイド（The Rust Programming Language）の[記述](https://github.com/rust-lang/book/commit/7e49659102f0977d9142190e1ba23345c0f00eb1)（まだpublishされてない？） 
    * "the name of each enum variant that we define also becomes a function that constructs an instance of the enum"
  * 公式Rustリファレンスの記述に関する解釈 
    * Qiitaの記事「tuple struct, enumは暗黙にコンストラクタをクロージャとして定義する」の [コメント欄](https://qiita.com/osanshouo/items/cad7fb3dc91a5e95e578#comment-8e12d4a1758aac3995e1)より。『「タプルのコンストラクタは関数である」という直接的な記述は見つからなかったのですが、`call expression`が可能 = 関数の一種と見なせるという理屈で理解できます』

これらに書かれていることをまとめると以下のようになります。

  1. enumのタプルアイテムやタプル構造体の初期化式は、そのコンストラクターの `call expression` に等しい 
    * つまり`Some(x)`による値の初期化は、関数ポインター`fn(T) -> T`を呼び出しているとみなせる
  2. safeな関数ポインターは`Fn`, `FnMut`, `FnOnce`を実装するので、`fn(T) -> T`を`map`に渡せる


CC BY-SA 4.0
2021-09-08T03:22:03.593
82332
2021-09-08T04:34:06.813
2021-09-08T04:34:06.813
14101
14101
82330
answer


> いくらfun(x)とSome(x)で書き方が似ていようと、前者はクロージャ、後者はenumです。どうして上記の書き方でうまくいくのでしょうか？

リファレンスの[Function item types](https://doc.rust-
lang.org/reference/types/function-item.html)の項目にそれらしい記述を見つけました。

  * 関数とenumのコンストラクタがともにfunction itemとして扱えること
  * function itemが`FnMut`を実装すること（関数ポインタが`FnMut`を実装する遠因？）

が書かれています。

また[Function pointer types](https://doc.rust-lang.org/reference/types/function-
pointer.html)の項目にはfunction
itemから関数ポインタがつくれるとあるので、上記と合わせて、`map`の引数として`Some`を渡して良いことがわかります。

ところでエラーを起こしてみると、実際にどのような型がついているかがわかります。

[code]

    fn main() {
        Some
    }
    
[/code]

[code]

    error[E0308]: mismatched types
     --> test.rs:2:5
      |
    1 | fn main() {
      |           - expected `()` because of default return type
    2 |     Some
      |     ^^^^ expected `()`, found fn item
      |
      = note: expected unit type `()`
                   found fn item `fn(_) -> Option<_> {Option::<_>::Some}`
    
    error: aborting due to previous error
    
    For more information about this error, try `rustc --explain E0308`.
    
[/code]


CC BY-SA 4.0
2021-09-08T05:41:12.920
82335
2021-09-08T05:41:12.920
13199
82330
answer