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
- 文字列リテラルはその参照である
&str
const 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章まで眺め終わった。
ほんと「眺めた」程度だ。
全然実装できる気がせんなぁ。