こんにチュア!本記事は Rustアドベントカレンダー2025 1日目の記事です!
本記事では、ゆめみ社内開催のRust勉強会にて自分が出題したミニクイズをまとめました!面白いネタがあったら同僚に出すような、小ネタ集として見てくれたら幸いです ![]()
まだRust勉強会は続くためナンバリングが①になっています。今日から新会社所属になりましたが、今後とも変わらずRust勉強会を実施できたら②の記事が書けるかも...?書けることを願っています!
【宣伝】hooqアドベントカレンダー走行中!
筆者は本アドカレとは別に hooqアドベントカレンダー という筆者が作ったクレートhooqを紹介する個人アドベントカレンダーも執筆してます!もし良かったら覗いてみてください ![]()
Rust勉強会概要
社内Rust勉強会は、 Rustlings に沿って進めています。そのため、各クイズもなるべくその回に沿った内容です!よって、参考のためにRustlingsの該当回も付記しておきます。
Let's クイズ!
では本題のクイズに入りましょう!
第1問 チュートリアル回
初回は第0回として問題は解かず、Rustの紹介を行いました!というわけでRustという名前の由来に関する問題です。
問題
Rustの名前の由来の一つに、"tRUST" や "RobUST" など信頼や堅牢といった言葉に近いからというのがあります(要出典)。
もう一つ、明確な由来とされているものがあるのですが、それは以下の3つのうちどれでしょう?
- 「最後のプログラミング言語」を目指し、"last"と掛けた
- 開発メンバーがゲームオタクで、ゲームのRustから取った
- 開発メンバーが自転車好きで、チェーンの"錆"から取った
答え
答えは「3. 開発メンバーが自転車好きで、チェーンの"錆"から取った」。
設計者のグレイドン・ホアレさんが生物オタクでもあるために"さび菌"を由来ともしているらしい。(さび菌はしぶといとされる)
第2問 変数宣言
まずは変数宣言から、というわけで、ミュータブル・イミュータブルに関係するような問題を出してみました!
rustlings: 01_variables
問題
Rustの次のコードに一番近いTSコードはどれでしょうか...?
let v: Vec<i32> = vec![1, 2, 3];
let v: number[] = [1, 2, 3];const v: number[] = [1, 2, 3];const v: readonly number[] = [1, 2, 3];
答え
答えは 3
const v: readonly number[] = [1, 2, 3];
Rustのイミュータブルは、配列の中身に関しても守られます。
安心して let に身を任せてしまいましょう!
第3問 関数
関数の返り値省略時のシグネチャを問いました!この手の知識って後々効いてくる感があります...
rustlings: 02_functions
問題
Rustの関数は何も値を返さない時、返り値の型を省略できます。
fn hoge() {/* 処理 */}
省略しなかった場合の上記 hoge 関数のシグネチャはどれでしょう?
fn hoge() -> Nonefn hoge() -> ()fn hoge() -> !
答え
正解は 2. fn hoge() -> ()
() はユニット型といい、C言語の void 型のような、何もないことを示す型です。
-
None:Option列挙体のバリアントの一つ。型名ではない-
fn hoge() -> Noneというシグネチャは(基本的に)ない
-
-
!: Never型という型。そもそも返り値を 返さない ことを示します-
fn hoeg() -> !というシグネチャの関数は作れます
-
第4問 データ型
Rustの整数型の型名はビット数が入っているのが特徴的だったため、そこにフィーチャーしたコンサイ的な問題を出してみました!
rustlings: 04_primitive_types
問題
-
Q1.
u64型の最大値はいくつでしょう? -
Q2.
i32型の最小値・最大値はいくつでしょう?
答え
- Q1.
u64の最大値
2^64 - 1 = 18446744073709551615
-
Q2.
i32- 最小値: - 2^31 = -2147483648
- 最大値: 2 ^ 31 - 1 = 2147483647
-
符号付き整数は2の補数表現です
-
各型の
MAX,MIN定数で知ることができます
(補足) 2の補数表現
- 負数を「正の数をビット反転し1を足したもの」で表現
- 負数の最上位ビットは1に
- 1の補数と比べると
- 表現できる数の種類が1つ多い
- (1の補数には+0, -0がある)
- 正の数の減加算器がそのまま使える
- 表現できる数の種類が1つ多い
第5問 データ型 (2)
前問に続きデータ型の問題...と見せかけた謎問です!
rustlings: 04_primitive_types
問題
次の表現のうち、コンパイルエラーとなってしまうものはどれ?
( main 関数内にあるものとします)
let s = '';let n = 1_2__3___4____;let () = {};
答え
答えは1. let s = ''; です!
「空文字」という概念は存在しないのでコンパイルエラーです。
空文字列 "" なら問題ありません。
2: 整数リテラルはアンダーバーで区切ることができます
3: ブロック {} の返り値は () なので束縛可能です
ちなみにTypeScriptだと逆に2, 3がエラーで1だけ正常に実行されます。
第6問 ベクタ
Rustのドキュメントを読む習慣をつけてほしくて出した一題だったと思います。Vecのドキュメント をよく読めば解けます。( Deref<Target=[T]> や Clone トレイトのメソッドが混ざっているので最初は難しいかもしれませんが...)
rustlings: 05_vecs
問題
Vec<T> のメソッドでないものはどれ?
ヒントもとい補足:
v: Vec<T> について v.xxx(...) と書けないものを選んでください
mapjoinclone
答え
答えは 1. map です!
map は Iterator トレイトに定義されているメソッドで、Vec<T> からアクセスするには一旦 .iter() によって std::slice::Iter<'a, T> 型の値に変換する必要があります。
fn main() {
let v = vec![1, 2, 3];
let v_strs: Vec<String> = v.iter().map(ToString::to_string).collect();
println!("{}", v_strs.join(" "));
}
2の join はスライス &[T] から呼べるメソッドで、 impl Deref<Target = [T]> for Vec<T> {...} なので Vec<T> からも呼べます。
3の clone は Clone トレイトが提供するメソッドで、 impl Clone for Vec<T> {...} なので Vec<T> のメソッドでもあります。
第7問 所有権
最初の鬼門、所有権を扱った回でした!「Rustにおけるプリミティブ型とは...?」というのを確認できるような問題にしてみました。
rustlings: 06_move_semantics
問題
macro_rules! check_impl_copy {
($v:expr) => {
let v1 = $v;
let v2 = v1;
println!("{v1:?}, {v2:?}");
};
}
fn main() {
/* 1. */ check_impl_copy!(10);
/* 2. */ check_impl_copy!('\u{3042}');
/* 3. */ check_impl_copy!(vec![1, 2, 3]);
/* 4. */ check_impl_copy!([1, 2, 3]);
}
- ~ 4. のうちコンパイルエラーとなる行はどれでしょう?
補足: 例えば check_impl_copy!(true) は以下に展開されます
let v1 = true;
let v2 = v1;
println!("{v1:?}, {v2:?}");
答え
答えは 3. vec![1, 2, 3] です!
-
- 整数型は
Copyトレイトがついています
- 整数型は
-
- これは
char型で、Copyあり
- これは
-
VecはCopyを実装しません
-
- 配列は要素の型が
Copyを実装する時Copyです
- 配列は要素の型が
Copy トレイトの有無は各型のドキュメントで確認できます。
第8問 構造体・列挙体
struct と enum が登場し、なんとなく予約語を調べたくなったので出した問題です!
rustlings: (直接関係するような問題はなし)
問題
次のうちRustの予約語※で ない ものはどれ?2つ選べ
struct, self, Self, union, enum, yield, match, switch, type, priv, try, use, super, abstract, typeof
※ 予約語には、将来的な機能追加のために予約されているものを含む
答え
答えは union と switch です! (ただし The Rust Programming Language に準拠)
-
対応機能が存在する予約語
-
struct,self,Self,enum,match,type,use,super
-
-
将来のための予約語
-
yield,priv,try,abstract,typeof
-
-
switch:match式があるのでなし -
union: 機能はあるが予約語でない
(参考) 共用体 union
列挙体があるRustにとっては不要だが、C言語の共用体と互換性を持たせる(FFI)ために用意されている機能
#[repr(C)]
union MyUnion { // メモリ上では u8 も bool も1バイトを使う
i: u8,
b: bool,
}
fn main() {
let u = MyUnion { i: 42 };
println!("int: {}", unsafe { u.i });
println!("bool: {}", unsafe { u.b }); // 未定義の動作
}
union は変数として普通に( r# を付けずに)利用可能です。なぜ union の機能はあるのに予約語ではないのでしょうね...互換性の問題とか?謎です。
fn main() {
let union = 10;
println!("{union}");
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=51091808ece8b31a030808800c8a0a0f
第9問 列挙体
列挙体にかこつけて複合的に問う謎問になってしまいました...
rustlings: 08_enums
問題
次のプログラムは1~5のうち2行をコメントアウトするとコンパイルが通ります。どの2行でしょうか?
enum MyResult { Ok, Err }
use MyResult::{Ok, Err}; // 1
fn func() -> MyResult {
let res = Ok;
let _ = res; // 2
let _a = res; // 3
let _b = res;
let _ = Ok as usize; // 4
let _ = Err?; // 5
Ok
}
答え
正解は 3番 と 5番 です!
enum MyResult { Ok, Err }
use MyResult::{Ok, Err}; // 1
fn func() -> MyResult {
let res = Ok;
let _ = res; // 2
// let _a = res; // 3
let _b = res;
let _ = Ok as usize; // 4
// let _ = Err?; // 5
Ok
}
- 1:
use 列挙体型::{対象のバリアント, ...};で列挙体型は省略可能 - 2と3:
_変数名は束縛をするが、_は束縛を行わない- 参考: https://doc.rust-lang.org/reference/patterns.html#wildcard-pattern
- 所有権が奪われてしまう 3 をコメントアウトが正解
- 4: 全てのバリアントがユニット様である列挙体は整数型にキャスト可能
- (正直使い所はほとんどない)
- 5:
?演算子(tryマクロ)が使える列挙体はOptionとResultのみ- 正確には
Tryトレイト実装型だが、実質Option,Resultに限られる -
type MyResult<T> = Result<T, XXX>;のような型エイリアスの場合この限りではない-
anyhow::Resultなどが該当
-
- 正確には
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=4f4e94dde80758b6c76d2700f8e3d782
第10問 文字列
ちょうど案件か何かで文字列数をどう判定するかみたいなことで迷っていたので出した問題でした。 String::len はバイト数を返すのですよね...
rustlings: 09_strings
問題
次の文字列は何文字でしょうか...?
㍿👨💻ゆめみ👩💻
ヒント: ㍿ は1文字です
- 6文字
- 7文字
- 34文字
- 10文字
答え
- 6文字: ⚪︎ とりあえず正解!
- 7文字: C言語と違い
\0は含まれないので不正解 - 34文字: △ 34文字ではなく 34バイト
- 10文字: △ ユニコードのポイント数は10ポイント
"\u{337F}\u{1F468}\u{200D}\u{1F4BB}ゆめみ\u{1F469}\u{200D}\u{1F4BB}"
(ユニコード的な意味で)文字数を確認するコードです!
use unicode_segmentation::UnicodeSegmentation;
fn main() {
let s = "\u{337F}\u{1F468}\u{200D}\u{1F4BB}ゆめみ\u{1F469}\u{200D}\u{1F4BB}";
println!("{s}"); // 6文字のはず
println!("len: {}", s.len()); // 34
println!("chars len: {}", s.chars().collect::<Vec<_>>().len()); // 10
let g = s.graphemes(true).collect::<Vec<&str>>();
println!("graphemes len: {}", g.len());
}
...ごめんなさい厳密な答えはありません!というかこの問は「〇文字!」と定めてしまうとうるさい人から色々言われるので気を付けましょう...
第11問 モジュール
モジュール分け、その言語になれてないとストレスの元ですよね...なんていうことに思いを馳せながら作った問題だった気がします。
rustlings: 10_modules
問題
a::b モジュールがしっかりと存在するのはどのツリーでしょうか?
ヒント?: 答えは一つとは限らないです!
- 1
├── Cargo.toml
└── src
├── a
│ ├── b
│ │ └── mod.rs
│ └── mod.rs
└── main.rs
- 2
├── Cargo.toml
└── src
├── a::b
│ └── mod.rs
├── a.rs
└── main.rs
- 3
├── Cargo.toml
└── src
├── a
│ └── b.rs
├── a.rs
└── main.rs
答え
答えは 1 と 3 です!
-
mod.rsを使ったモジュール分けです。 - このような書き方はありません。
-
mod.rsを使わない場合の書き方です。
-
b.rs:b/mod.rsとわざわざする必要はないです -
a.rsとa/b.rs:- 2018 edition より利用可能に
第12問 ハッシュマップ・Option
程よいタイミングでHashMapとOptionの取り扱いが被ったので、両方を問えるような問いにしました!
rustlings: 11_hashmaps ・ 12_options
問題
HashMap::<K, V>::get(&self, k: &K) メソッドの返り値型はなんでしょう?
V?Option<V>Maybe<&V>- どれでもない
答え
答えは 4. どれでもない です!
get メソッドはキーに対応する値の 参照 を得るメソッドなので( &self がレシーバなのがヒント)、 &V にまつわる型が答えになります。
存在する・しないを表すのにはOption型を使います。
以上より、 Option<&V> が正解です。記法として1と3は(組み込みでは)存在せず、2は &V ではなく V なので不正解でした!
第13問 Option型
Option型を取り扱う折角の機会だったので、自分が(今でもまれに)よくやるミスを出題してみました!
rustlings: 12_options
問題
コンパイルエラーを探せ!
fn print_name(id: usize, members: &HashMap<usize, Member>) {
if let Member { name } = members.get(&id) {
println!("Member {id}'s name: {name}");
} else {
println!("Member {id} is not found.");
}
}
上記はコンパイルエラーになります。どこを間違えているでしょうか?
答え
let Member { name } = ... の部分が誤り!
Some がなく、正しくは let Some(Member {name}) = ...
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5821cddd432493f49b7cb8ffef1278c9
第14問 Result型
今回の問題はResult型というよりはトレイトの問題でした。とはいえ、特に最初に Result 型に触れた時に結構混乱しそうな内容なので、 Result 型を学ぶうちに触れておいて損はないと思い出題しました。初心者には少し難易度高めです。
rustlings: 13_error_handlings
問題
fn process() -> Result<(), ???> {
Ok(())
}
??? 部分に入れると必ずコンパイルエラーになるのはどれ?
std::ops::Range<()>std::error::Error()std::io::Error
答え
答えは 2. std::error::Error です!
他は型ですが、 std::error::Error は型ではなく トレイト なので、impl std::error::Error のように書く(静的ディスパッチの場合)か、Box<dyn std::error::Error> のようにトレイトオブジェクト(動的ディスパッチ)を使う必要があります!(大体後者が吉)
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f012be3ea0c570824c1c9895e655fcbe
第15問 Result型 (2)
難易度や内容的に、14問と15問は逆順で聞くべきだったかもしれませんが、当時の通りの順番で紹介しています。解き方は第6問の Vec 同様ドキュメントを確認することです。
rustlings: 13_error_handlings
問題
次のうち Result 型に存在しないメソッドはどれでしょう?
ヒント & 補足: "メソッド"はトレイト実装により提供されるメソッドを含みます
mapto_stringunwrapflatten
答え
答えは 2. to_string
Result<T, E> where T: Debug, E: Debug の時は Result には Debug が実装されますが、 Display トレイトはどの場合でも実装されません!
よって ToString トレイトも同様に実装されず、 to_string は使えないです!
Result<T, E> where T: Display, E: Display などは普通にあり得ます。
すなわち、「正常値やエラーは中身を取り出してから表示する必要がある」ということです!
第16問 ジェネリクス
今回最後の問題は第13問と同様にコンパイルエラーを探せ!問題で、自分がやりがちだったミス由来の問題になっています。結局コンパイラの気持ちになるのがRust習得の早道ですね(多分)!
rustlings: 14_generics
問題
コンパイルエラーを探せ!
struct Wrapper<T>(T);
impl Wrapper<String> {
fn show(&self) {
println!("{}", self.0);
}
}
impl Wrapper<T> {
fn func() {}
}
コンパイルエラーとなる原因箇所を指摘してみてください!
答え
impl に <T> が足りません!
impl Wrapper<T> {
fn func() {}
}
ジェネリクスは impl にくっつけて書く必要があります。
impl<T> Wrapper<T> {
fn func() {}
}
(補足) なぜ impl<T> と書くのか?
-
implブロックは複雑な場合もある- 例:
impl<T, U> Trait<T> for Type<U> {...}
- 例:
- 「ジェネリクスが埋まった 特定の型の時のみ実装を持つ」
みたいな書き方をする時もある- 例:
impl std::fmt::Display for Wrapper<String> {...} - 全ての
Wrapper<T>にDisplayが実装されるわけではない - impl横の表記抜きでは
Stringが型引数か型なのか判別できず
- 例:
以上の事情で利用するジェネリクスはimpl横で示すのがルール
まとめ
いかがでしたでしょうか...?ミニクイズは元々勉強会参加者を増やすために始めたものでしたが、案外様になっていて自分でも驚いてます。
- 新しいパラダイムが多い言語だから意外性を出しやすい
- 一方で変な構文が少なく、理解・解説がしやすい機能が多い
こんなRustの性質がミニクイズと合っていたかもしれません。
最後に、本記事執筆にきっかけをくださった勉強会関係の皆様、自由に勉強会を開催させてくれたゆめみさん、そして読者の方に感謝いたします。
ここまで読んでいただきありがとうございました!