rust: Rustがんばろう 18日目
2025/05/25
はじめに
Rust の勉強をがんばろうとしたのは本当なんだよ。
他にやりたいことがあったので忘れていただけだよ。
なお、Rust勉強シリーズは開発日記のインデックスからは削除した。 日記とはいえ、見て他の人が役に立つものでは無いし。
前回の復習
関数の引数で参照しない場合
関数の引数を参照にしなかった場合、変数を与えたらそれ以降使えなくなる、ということになる。
そんな用途ってあるっけ?
と思って作ってみたのだが、参照で無く受け取っているので所有権が hello()
に移ったかと思ったがそうならなかった。
あれ、何か考え違いしてる??
fn hello(x: i32) {
println!("Hello, {}!", x);
}
fn main() {
let x = 123;
hello(x);
hello(x);
}
Copy
トレイトを実装している型は関数の引数にしてもコピーが自動で行われるそうだ。
10.2章で出てきた話だ。当然私は忘れている。
- トレイト境界でlargest関数を修正する
-
i32やcharのようなサイズが既知の型は スタックに格納できるので、Copyトレイトを実装しています。
-
ジェネリックにした関数の心意気(?)として + Copy
を付けておくと Copy
トレイトを実装した型だけになるそうだから
hello()
もこうしておけば呼び出し元は今まで通りでよいということか・・・と思ったがエラーになった。
fn hello<T: Copy>(x: T) {
println!("Hello, {}!", x); // `T` doesn't implement `std::fmt::Display`
}
では + Display
として use std::fmt::Display;
を付ければよし。
use std::fmt::Display;
fn hello<T: Copy + Display>(x: T) {
println!("Hello, {}!", x);
}
fn main() {
let x = "abc";
hello(x);
hello(x);
}
ただ、let x: &'static str = "abc";
と元が参照になるためか Copy
トレイトが無くても動く。
let x: &str = "abc";
でも結果は同じだった。
&'static str
は vscode が自動で出してくれたのだが、書き方が分からない。
10.3 章の後ろの方に出てくるので、今はスルーする。
10.3章の続き
前回は借用チェッカー付近までだった。
参照になっている変数にはすべてライフタイムという概念がある。
静的に評価できる概念なので間違っていればコンパイルではじかれる。
ライフタイムは参照が有効なスコープのこと。
そもそもスコープから外れたらだいたいのものは無効になるんじゃないの?
「スコープ」だけだとコンパイルして無効にする理由にならないから「ライフタイム」という名前を付けてチェックするようにしたのかしら。
記号として '
を使うことで明示的に表すこともできるし。
次に進もう。
関数のジェネリックなライフタイム
この話のポイントは、引数を使った戻り値で Copy を伴わないものというところか?
戻り値に引数が使われないのであれば単に引数を参照するだけで済むからだ。
しかし参照した引数をそのまま返すだけでもエラーになるのは分かりづらい。
リスト10-21 の何がダメなのか。
// list 10-21
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
戻り値のライフタイムの指定が無いからだという。
ドキュメントよりもコンパイラが新しいからか、対処法まで出力されていた。
1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
| ++++ ++ ++ ++
x: &str
と y: &str
のライフタイムが同じとは限らない、ということだろうか?
関数的に引数から戻り値まで、というわけにはいかないのだろうか。
fn longest(x: &str, y: &str) -> &str
: エラーfn longest<'a>(x: &'a str, y: &'a str) -> &'a str
: OK
それぞれの引数と戻り値を &'a str
にすることで解消できる。
それぞれの &str
が別のライフタイムでは無く 'a
というライフタイム名を付けて全部同じだと示すことに意味があるのか。
関数としては本当に引数のライフタイムが両方とも同じかどうか判断できないんじゃ無いのか、と思ったが、
意味としては「少なくともライフタイム'a
と同じだけ生きる」ということだそうだ。
ライフタイム名の宣言 <'a>
などは関数の中身では無くシグネイチャ、つまり引数や戻り値のライフタイムはこれこれですよと告げるだけで、
中身はそのまま評価されるので、ローカル変数の参照はダメなのはそのままだ。
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}
これって、ライフタイムを別の期間にするような使い方をすることはあるのだろうか?
と思ったら 10.3章の後ろの方にあるな。