20250818101134 learning rust
何度も挫けているが、rustの勉強をしたい。 挫けている理由の一つに、チュートリアル的なドキュメントを読んでいると疑問が大量に出てきて前に進めなくなる、というのがある気がする。気になったことをかたっぱしからここに書いて、調べつつ読み進めることにしてみる。
学習リソース
- とりあえずこれを読むべき、という記事をよく見る。20250820 今はこれを読んでいる: The Rust Programming Language 日本語版 - The Rust Programming Language 日本語版
- 入出力プロジェクト、というのは個人的にはあまり興味を持てなかった。
- 版が古い。 この章はまるごと存在しない。 Fundamentals of Asynchronous Programming: Async, Await, Futures, and Streams - The Rust Programming Language
- Rust ツアー - Let’s go on an adventure!
- 気になることを検索すると、よくヒットする: Introduction - Rust By Example 日本語版
- 個人的に信頼度が高い m.hiroi氏のページ [Linux Programming / お気楽 Rust プログラミング超入門](https://www.nct9.ne.jp/m_hiroi/linux/rust.html
- This is a free Rust course developed by the Android team at Google. Comprehensive Rust 🦀 へようこそ - Comprehensive Rust 🦀
- この講座を受講した。 Ableton and Max Community Japan #061「MIDIコン開発入門」出演: Akiyuki Okayasu - 御茶ノ水RITTOR BASE
- メモはあるがどこまで公表してよいか、なんとも言えないのでこのリンクはprivate 20250427142104 MIDIコン開発入門
- 組み込み用フレームワーク Embassyというのを使用した。 (https://embassy.dev/)
- How not to learn Rust
- Introduction - Asynchronous Programming in Rust
- Introduction - The Rustonomicon
References
Playground
気になったこと
プロジェクトのファイル構成について
rust bookにはこのように書かれている。 リファクタリングしてモジュール性とエラー処理を向上させる - The Rust Programming Language 日本語版
- プログラムを_main.rs_と_lib.rs_に分け、ロジックを_lib.rs_に移動する。
- コマンドライン引数の解析ロジックが小規模な限り、_main.rs_に置いても良い。
- コマンドライン引数の解析ロジックが複雑化の様相を呈し始めたら、_main.rs_から抽出して_lib.rs_に移動する。
これの出典はどこかにあるのだろうか? たとえば pythonだと決まっている 20240311211049 pythonのプロジェクト構成
cargoが作るのが基本、と考えればいいかもしれない。
これも参照。20250823152740 パッケージ(package)、 crate 、 モジュール(mod) という用語とそれぞれの定義(rust的な意味で)
naming convention
RFCで規定されている。
rfcs/text/0430-finalizing-naming-conventions.md at master · rust-lang/rfcs
識別子(identifier)として利用可能な文字
1.53以降ではunicodeが使える。[Rust] 円周率の表記 - 鴨川のはりねずみ
- や # などの記号類は、 C/C++と同様、 _ 以外は使えない。これの最新の資料が見つけられないが、
古いバージョンのrustドキュメントには記載されている。 Identifiers - The Rust Reference
Raw identifiers というのを利用して、一部の予約語を使うこともできる。 Rust でキーワード(予約語)を識別子として使う方法 (raw identifier)と、その例外
アトリビュートって何?
固定長のarrayある?
ある。 https://doc.rust-lang.org/std/primitive.array.html
グローバル変数はどうやって作るのか?
グローバル変数は簡単に作れない。
C++のtemplate部分特殊化に対応する機能/構文はある?
おそらく完全に一致するものは無い。
rust book list 10-10 のように限定的には可能。 cf. ジェネリックなデータ型 - The Rust Programming Language 日本語版
C++のconceptに対応する機能はある?
Trait Bound で実装できると思う
unionあるの?
ある。 Union s - The Rust Reference あんま推奨されないがC APIで必要になることも。
#[repr(C)] // あってもなくても結果は変わらなかった @ rustc 1.85.1 (4eb161250 2025-03-15)
union MyUnion {
f1: u32,
f2: f32,
}
fn main() {
let u = MyUnion { f1: 42 };
unsafe {
println!("MyUnion.f1: {}", u.f1);
println!("MyUnion.f2: {}", u.f2);
}
}とかやると、
MyUnion.f1: 42
MyUnion.f2: 0.000000000000000000000000000000000000000000059
PODのstructはCopy Traitを実装しなくてもcopyされるか?
されない。 PODという言い方なのだろうか。たぶん違う。Cの概念。 単純, 標準レイアウト, POD, およびリテラル型
// PODのstruct
struct MyStruct {
v1: u32,
v2: f32,
}
fn main() {
let s = MyStruct { v1: 42, v2: 3.14 };
println!("MyStruct.v1: {}", s.v1);
println!("MyStruct.v2: {}", s.v2);
let t = s; // この操作はmove i32とかだとcopyになる。
println!("MyStruct.v1: {}", t.v1);
println!("MyStruct.v2: {}", t.v2);
// moveされているので以下はコンパイルエラー
// println!("MyStruct.v1: {}", s.v1);
// println!("MyStruct.v2: {}", s.v2);
}sliceと型強制
rust bookは 20250819 時点で、配列のスライスを取得するには
let a = [1, 2, 3, 4, 5];
let slice: &[i32] = &a[..]のように書く、と書いてあるが、実際には
let a = [1, 2, 3, 4, 5];
let slice: &[i32] = &aでも行けた。 rustc 1.85.1 (4eb161250 2025-03-15) この方が便利で、Stringなどをそのまま &str を引数に取る関数の引数として使用可能。この機能は「型強制という。 cf. 型強制
rustではVecやStrのような配列を扱う関数を書く時には、スライスを引数とすることが推奨されている。 cf. Rustで関数の仮引数の型をスライス(&[T], &str)にする | rs.nkmk.me
if letが直感的ではない
そう思った人がメンタルモデルを含めて詳しく書いてくれている。Rustの「if let」とは何なのか? - Qiita
bookの8.3を読んでいて思った。こういう時に便利。 ハッシュがあった時だけ表示する。とかが簡単に書ける。
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("blue");
if let Some(score) = scores.get(&team_name) {
println!("Score for {}: {}", team_name, score);
}
}
パッケージ(package)、 crate 、 モジュール(mod) という用語とそれぞれの定義(rust的な意味で)
20250823152740 パッケージ(package)、 crate 、 モジュール(mod) という用語とそれぞれの定義(rust的な意味で)
cargo new で workspace作れないの?
今(20250908) はできない。昔からある議論らしい。 activeなissue: Add `cargo new —workspace` to create a workspace · Issue #8365 · rust-lang/cargo
Traitのデフォルト実装を特殊化したケースの関数内から呼ぶことは可能か?
この人と同じことを期待したが、そのままではできない。Call default trait method impl from specialized impl
あるデータ型にどのようなTraitが実装されているかを調べる方法はあるのか?
- 無いかもしれない Display all traits for a variable
そもそもRTTIあるのか?
おそらく無い(し、そのような実装はrust的では無い) r/rust - Reddit
downcast的なことは可能 Rust の std::any::Any トレイトを用いて安全に動的型付けっぽいことをやる - Qiita
println! などで使える placeholder {}
要調査 {:?} とか {:#?} とかある。 hexで表示したい、とか0詰めしたいとかの場合はどうしたらいいの?
ここにまとめられている。 std::fmt - Rust
ライフタイムを省略できるのは、どんな場合?
この機能は lifetime elision という。
どういう時に省略可能なのかは、 この説明がわかりやすかった。 Rustのライフタイム推論入門
book の ライフタイム省略 も参照。 ライフタイムで参照を検証する - The Rust Programming Language 日本語版
最初の規則は、参照である各引数は、独自のライフタイム引数を得るというものです。換言すれば、 1引数の関数は、1つのライフタイム引数を得るということです: `fn foo<'a>(x: &'a i32)`; 2つ引数のある関数は、2つの個別のライフタイム引数を得ます: `fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`; 以下同様。
2番目の規則は、1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入されるというものです: `fn foo<'a>(x: &'a i32) -> &'a i32`。
3番目の規則は、複数の入力ライフタイム引数があるけれども、メソッドなのでそのうちの一つが`&self`や`&mut self`だったら、 `self`のライフタイムが全出力ライフタイム引数に代入されるというものです。 この3番目の規則により、必要なシンボルの数が減るので、メソッドが遥かに読み書きしやすくなります。
この規則を適用して、全部の引数と戻り値にライフタイムが設定できる場合は省略できる。
for loop with indexどうやるの?
enumerate(), zip() が使える。 Rust インデックス付きで Vec, イテレータをループ, 走査する方法
typedef あるの?
ある。
type MyInt = i32;
ただし、alias。別の型ではない。(そこもC/C++と同じ)
Does Rust have an equivalent of C’s typedef?
Boxっていつ使うの?
スマートポインタ全体的に謎いから整理したほうがいいかも。
- Bookの例のようにListを定義したい時には、そうするしかない。 ヒープのデータを指すBox を使用する - The Rust Programming Language 日本語版
正直、使うケースは少ないと思います。
スタックに積めばいいものをわざわざヒープに確保する事は無駄です。
アンダースコアいつ使うの?
- matchのデフォルトケース
let x = Some(5);
match x {
Some(1) => println!("one"),
Some(n) => println!("number: {}", n),
_ => println!("anything else"),
}- 未使用変数の先頭に付けると未使用警告が出ない
let _hoge = 0;
// 使わないこれは使うことになったら変数名を変更する必要があって、ちょっと微妙だと思った。
- returnされた値を使わないから無視したい
といいつつ、このコードは警告も出ない。
fn add(x: i32, y: i32) -> i32 {
x + y
}
fn main()
{
_ = add(1, 2); // _ は無くてもコンパイルエラーにならない
}
このくらいやると警告が出る warning: unused Result that must be used
fn add(x: i32, y: i32) -> Result<i32, String> {
Ok(x + y)
}
fn main()
{
add(1, 2); //
}- タプルの使わない要素を無視
fn main()
{
let tuple = (1, 2, 3);
let (x, _, z) = tuple; // _ を y とかにすると未使用の警告が出る
println!("{}, {}", x, z)
}- 使わない引数を無視 トレイトの実装とかでインターフェースを揃える時に必要そう
fn function(x: i32, _: i32) -> i32 {
x // 2番目に名前を付けると警告が出る
}- コンパイラに推論を任せられるけど記述が必要な場合
let numbers = (0..10).collect(); // これはできない。 error[E0283]: type annotations needed
// collectで作れる データ構造がいっぱいあって、どれかわからない。
// 例えばこのようにしろ、というヒントがエラーに表示される。 Vec であることまで伝えれば良い。 (VecDequeとかでもいい)
let _numbers: Vec<_> = (0..10).collect();ライフタイムでも使うことがある、とのこと。 Rust: 匿名ライフタイム - TAKOYAKING’s blog
未調査
- .. いつ使うの?
- トレイトまとめたトレイト作れる?
- operator実装できる?
- バイナリファイルを読み書きしてみたい
- 開発環境
- vscode以外は何があるのか?
- vscodeにはメモリマップは無かったような気がするが困らないのか
- vscode以外は何があるのか?
- unitって何
- singleton作れるのか?
- インラインアセンブラ
- so/dll
- C interop
- C++ interop
- python interop
- cross compiling
- 並行/並列処理
- ccacheみたいなビルドキャッシュある?
- GUI
- audio
個人の感想
所有権について
所有権の概念は、C++を使っている人は 「rustの変数は基本的にいつでもstd::unique_ptrの問題点を無くしたものを使っている」 と思うと分かりやすいかもしれない。 std::move とかを使いはじめてイライラしたことがあると、何をしたかったのか分かりやすい。
- cf. ここの「Rust による解決」 までを読むとわかりやすい Rustは何が新しいのか(基本的な言語機能の紹介)
関数などの引数もunique_ptrだと思っておけば、「コピーは基本的にできない。参照渡しのみ」というのも個人的には理解しやすかった。むしろC++と比べたらsyntax sugarで書きやすくなっている。
- C++でも参照渡しを基本にするべきだし、constの有無を丁寧にやった方が良い。この「やった方が良い」をやりやすくしているのがrust。
これを3回くらい読む。 所有権を理解する - The Rust Programming Language 日本語版
ヘッダファイルが無くて素晴しい
実装内容を1つのファイルに書けるのがとても良い。C++だと#includeで絶妙に循環参照を避けていたケースはどうやって運用すればいいのだろう、というのが若干気になるが、他の言語だと大抵1つのファイルに書いているので、問題となることは無さそう。「適切にファイル/モジュールを分割せよ」ということに尽きる。
Optionalが素晴しい
個人的にずっとboolは true, false, nil(無効) の3値で扱われるべきと思っていたので、それが実現されている点はとても良いと思う。 schemeだと nil は '() で代用されていた。
enumが素晴しい
enumが列挙だけではなく、型+値を保持できるのは便利。ステートマシンを書く時に便利だと思う。
matchが素晴しい
enum + match の世界観はアプリケーションロジックを書く時に便利だと思う。
クロージャ
クロージャなのにtrait名が Fn とかなのが謎い