rust: Rustがんばろう 8日目
2025/02/21
はじめに
Rust を勉強することにした。
わざわざブログに書いているのは、あのとき私はこう思っていたんだ、と初心者だった自分を楽しみたいからかもしれない(初心者から脱せたとして)。
ファイル分け
前回書いたこちらを 2つのファイルに分けよう。
mod abc {
fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
pub mod def {
pub fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
}
}
fn hello(label: &str, value: &i32) {
println!("{}: {}", label, value);
}
fn main() {
let m = 10;
abc::def::hello("m", &m);
}
分割1
- hello.rs
// hello.rs
pub mod abc {
fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
pub mod def {
pub fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
}
}
- main.rs
// main.rs
mod hello;
use hello as abc;
fn hello(label: &str, value: &i32) {
println!("{}: {}", label, value);
}
fn main() {
let m = 10;
abc::def::hello("m", &m);
}
これはダメだった。
- import 文的な
modで使うのはファイル名?front_of_house.rsはpub mod hostingなのにmain.rsではmod front_of_house;なのでたぶんそう。- ディレクトリが別のときはどうするんだろう?
hello.rsのsuper::はダメ。- まあ、これは呼び出し関係ではなくパスだから仕方ない
crate::に置き換えるとエラーは消えたのでmain.rsの方を参照できたのか?- ということは
main.rsに置いた関数はpubとかなくてもどこからでも見える?
main.rsのabc::def::hello()でdefがエラーになるpub mod defなのだがダメなのか?
最後の解決法が分からない。
failed to resolve: could not find `def` in `abc`
could not find `def` in `abc`
同じモジュールにいたときには呼び出せたので、そういうものというだけ?
あれ、そういえば hello.rs に直接 pub fn hello() を書いたら main.rs からどうやって呼び出すんだろう?
これは abc::hello() で呼び出せた。
ああああ、そういうことか。
hello.rs の中にある mod abc を参照しているつもりだったけど、実は hello.rs そのものがモジュールになっているのか。
7.5章のサンプルコードもそうなっているではないか。
分割2
- hello.rs
// hello.rs
pub mod abc {
fn hello(label: &str, value: &i32) {
println!("{}: {}", label, value);
}
pub mod def {
pub fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
}
}
pub fn hello(label: &str, value: &i32) {
abc::def::hello(label, value);
}
- main.rs
// main.rs
mod hello;
fn main() {
let m = 10;
hello::hello("m", &m);
hello::abc::def::hello("m", &m);
}
ちょっと形は変わったが、これは動く。
慣例に従うなら abc::def は use を使うべき、というところかな。
ディレクトリ分け
続けて、モジュールを別のディレクトリに置くやり方も書いてある。
- rsファイル名がモジュールになるのは同じだが、その中に書いていた実装は
pub modの行だけにする - rsファイルと同じ名前のディレクトリを作る
- ディレクトリの中に
モジュール名.rsのファイルを作る - そのファイルには
pub modで書いていた中身だけを書く
分割3
- hello/abc/def.rs
pub fn hello(label: &str, value: &i32) {
super::hello(label, value);
}
- hello/abc.rs
pub mod def;
fn hello(label: &str, value: &i32) {
println!("{}: {}", label, value);
}
- hello.rs
pub mod abc;
use abc::def;
pub fn hello(label: &str, value: &i32) {
def::hello(label, value);
}
- main.rs
mod hello;
use hello::abc::def;
fn main() {
let m = 10;
hello::hello("m", &m);
def::hello("m", &m);
}
mod ほげほげ { ... } と囲むのではなく、ほげほげ.rs に中身を書くという感じか。
そのモジュールA がさらにモジュールB を含んでいる場合は Aと同じ名前のディレクトリを作って、名前B の rsファイルを作る、と。
クレートルートを持っていなければどんなにしっかり実装しても単なるモジュールツリーだ。
コレクション
よくあるのが vector, list, map か。文字列はコレクションに入るのかよくわからないな。
Vec- 対象の型を右辺に指定しないんだ
- みたいなことを前もどこかで思った気がするから全体的にそういうしくみなんだな
- 「最初の要素への不変参照」と最初なのを強調しているが
&v[1]でも同じエラーになった- オリジナルも “a reference to the first element” なので、単にコード上で最初の要素を使ったからそう書いただけか
- realloc したときにアドレスが変わるという理由なら、先頭だけエラーになるということはないな
- そういえば、参照している間はそのメモリを解放しない、という方向ではないんだな
push()後に参照する変数を用意するのは大丈夫
let mutだとv[1] = 10;のような変更はできる- が参照する変数を作ったあとに代入するとエラーになる。
- アドレスが変わる可能性があるからダメ、ではなく、そういう可能性があるから参照後の変更は許可しないルールになったということだな
- そもそも、人から借りたものを汚したりしないようにするのは当たり前のことだ
- が参照する変数を作ったあとに代入するとエラーになる。
- 参照する変数を作っても、それ以降で使わなければエラーにならない
- まあ warning にはなるし意味は無いがね。
print!()では{:?}で中身を全部列挙してくれた- 値あり
Enumにして複数の型を持たせる、みたいなこともできる- C/C++ なら
unionにしそうなやつだ - JSONデータの読み込みみたいな型がよくわからないときに使えるのか?
- C/C++ なら
- 対象の型を右辺に指定しないんだ
- 文字列
- プリミティブ型は
str- 文字列リテラルはその参照である
&strconst S: &str = "hello";
- 文字列リテラルはその参照である
Stringは構造体let s: String = String::from(S);- 中身は
vec: Vec<u8>だけ
- リスト8-12 の型を見ておこう
let data: &str = "initial contents"; let s: String = data.to_string(); let s: String = "initial contents".to_string(); str型のデータは作れそうな気がしない- ドキュメントも “It is usually seen in its borrowed form” といってる
&Stringは&strにキャストされることがある- From とか Into とかの話?
- add は
+のオペレータオーバーロード的なもの?impl Add<&str> for String { type Output = String; #[inline] fn add(mut self, other: &str) -> String { self.push_str(other); self } }let s1 = s1 + s2;みたいに使った方がよいかもs1がmutならs1 += &s2;とも書ける。このためだけにmutにするほどでもないか。
- 「まとめると、文字列は込み入っています。」
- まったくその通りやね。
- 文字として扱いたいとき、困ったら
charsを使うということらしい。
- プリミティブ型は
HashMap- map はどこの実装でも hash map なんだろうか。
- C++ の unordered_map には “hash” があるが map にはないので普通の map か
- 昔は
unordered_mapとかなかったなぁ(C++11からっぽい)
- 昔は
- C++ の unordered_map には “hash” があるが map にはないので普通の map か
newのときに型を書かなくても良いらしい- その代わり、次に
insertした型で固定される Vecのときには指定がいりそうなことを書いていたがなくてもよかった。事情は同じだ。
- その代わり、次に
HashMap<_, _>の行は、Key だけのVecと Value だけのVecからHashMapを作っているっぽいうまいこと型変換お願いします <(_ _)>とおじぎした顔文字ではない
has()やhasKey()ではなくentry()- 戻り値の
Entry<'_, K, V>はどういう書式だっけね。。。- ライフタイムというものらしい。10章なのでセーフだ。
- 古い値に基づいて値を更新する のやり方は自分で考えつくことができる気がしない。。
&mut Vを返すけど、受け取る方は参照先を変更するわけじゃないからmut無しでよいのか- Rust はインクリメント演算子系はないのだね。
- あれはアセンブラで積極的に
inc系のオペコードを使いたいためと思っていたけどどうなんだろうね。コンパイラは賢いので少なくとも今だとやってくれるだろう。 - 文中に書けるのは、まあ便利と言えなくもないけど危険な方が多そうだから積極的には使わんな
- あれはアセンブラで積極的に
- 戻り値の
- ハッシュ計算は BuildHasherDefault が使われるそうだ
- アルゴリズムは分からんかった。
- 変更することはできるらしい
- map はどこの実装でも hash map なんだろうか。
おわりに
ようやく 8章まで眺め終わった。
ほんと「眺めた」程度だ。
全然実装できる気がせんなぁ。
writer: hiro99ma