本題の前に一つ疑問が...
配列の中身がcharではなくi32なら上手くいきます。
次のコードを試してみましたが、同様のエラーが出ました。純粋に気になるので、どのようなコードで試されたか教えていただけますか...?
rust
1use std::collections::HashSet;
2
3fn main() {
4 let set: HashSet<i32> = [1, 2].into_iter().collect();
5}
原因
本題です。これが成功しない原因はご推察の通り余計な参照がついていることです。 HashSet<char>
は、 char
のイテレータからは作れますが &char
のイテレータから作ることはできません。
ではなぜ char
ではなく &char
が返ってくるのか。実は into_iter()
は別に全くムーブすることを要求しません。
into_iter()
は IntoIterator
トレイトにより提供されます。現在対象となっている ['a', 'b']
は [char; 2]
ですが、 ドキュメント によると、[char; 2]
に対する直接の実装はありません。しかし次の実装はあります。
rust
1impl<'a, T> IntoIterator for &'a [T; 2] { type Item = &'a T; ... }
ということでレシーバが勝手に参照に変換され、この実装を使用して &'a char
を生成するイテレータを作ります。
into_iter()
に所有権をムーブする印象があるのは、おそらく Vec<T>
に関して into_iter()
が所有権を移動する挙動をとるからだと思いますが、これは実際に、わざわざ
rust
1impl<T> IntoIterator for Vec<T> { type Item = T; ... }
というふうに所有権をとるような実装をしているからです。
解決
おっしゃる通り char
も Copy
を実装していますので、明示的にデリファレンスしてやることで型を合わせることができます。
rust
1let set: HashSet<char> = ['a', 'b'].into_iter().map(|x| *x).collect();
あるいは、コメント欄で qnighy さんからご指摘を頂いたように cloned()
を使う方法もあります。これはイテレータを受け取って、各要素を clone()
したものを生成するイテレータを返す関数です。ドキュメントによると、少なくとも整数に関しては上と等価です。こちらの関数を使うのが Rust 的には自然と言えるでしょう。
rust
1let set: HashSet<char> = ['a', 'b'].into_iter().cloned().collect();
Copy
トレイトが i32
などに書かれていない問題ですが、改めて 最新のドキュメント をあたってみるときちんと対応されていました。あなたがご覧になっているリファレンスは URL を良く見ると Rust 1.25 時点のものです。 Google などで検索して辿りつくページはなぜかこのバージョンがすごくまちまちで、いつも困ります。ここは常に最新なので、ふだんはこちらをブックマークして、ドキュメント内の検索機能を使うとか、ターミナルに rustup doc --std
と入力することであなたのパソコン内にあるオフラインのドキュメントを開くこともできます。
原因について調べると こちら が見つかりました。Copy
トレイトはコンパイラによって特別扱いされるトレイトですので、普通のトレイトとは訳が違います。ドキュメントは ソースコードから 生成されますが、プリミティブについてはコンパイラ内で扱われるビルトインなものだったので明示的な実装がなく、ドキュメントにはのっていなかったようです。現在は明示的な実装が置かれたため、ドキュメントにのりました。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/09/23 14:11
2018/09/23 23:22 編集
2018/09/24 01:05
2018/09/24 06:13 編集