前回は、所有権のルールとムーブについて学んだ。
今回は、同じ値を何度も使うためのクローンと、所有権を移動せずに値を貸し借りする借用(参照)について学ぶ。
学習の進め方
前回と同じく、2段階で進める。
1. 体験する(trials/)
- ブログ記事を読みながら、
trials/06_borrowing/で自由にコードを試す - 自分で書き換えたり、わざとエラーを起こしたりして実験する
2. 練習する(exercises/)
- 体験が終わったら、
exercises/06_borrowing/で構造化された練習問題に挑戦する todo!()の部分を自分のコードに書き換えて完成させる- 解答例も用意されている
準備
# リポジトリを最新の状態にする
cd /workspaces/rust-practice
git pull
# 体験用プロジェクトに移動
cd trials/06_borrowing
# VSCodeでファイルを開く
code src/main.rs
# 動作確認
cargo runクローン:中身を複製する
前回、Stringは代入のたびに所有権が移動すると学んだ。もし中身を複製して、両方使えるようにしたいなら、.clone()を使う。
fn main() {
let s1 = String::from("こんにちは");
let s2 = s1.clone(); // 中身を複製する
println!("s1 = {}", s1); // OK
println!("s2 = {}", s2); // OK
}s1 = こんにちは
s2 = こんにちは.clone()は、ヒープ上のデータまで丸ごとコピーする。確実だが、データが大きいほど処理に時間がかかる。何でも.clone()で解決するのではなく、次に説明する借用を使うほうが適切な場面も多い。
借用:値を貸すだけで渡す
所有権を移動させず、値への「参照」を渡す方法がある。変数の前に&を付ける。
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 参照を渡す(借用)
println!("'{}' の長さは {}", s, len); // sはまだ使える!
}
fn calculate_length(text: &String) -> usize {
text.len()
}'hello' の長さは 5calculate_lengthはtext: &String、つまりStringへの参照を受け取っている。
所有権は移動しないため、関数を抜けても呼び出し元のsはそのまま使える。これを借用と呼ぶ。「値を貸しているだけで、所有権は渡していない」というイメージである。
可変な借用
借用した値を変更したい場合は、&mutを使う。
fn main() {
let mut s = String::from("hello");
add_world(&mut s);
println!("{}", s);
}
fn add_world(text: &mut String) {
text.push_str(", world");
}hello, world元の変数sもmutで宣言し、渡すときも&mut sと書く必要がある。
借用のルール
借用には、安全のための重要なルールがある。
- 同時に持てる可変参照(
&mut)は1つだけ - 不変参照(
&)は同時にいくつでも持てるが、可変参照と同時には持てない
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // ← エラー!
println!("{}, {}", r1, r2);
}error[E0499]: cannot borrow `s` as mutable more than once at a time
(略)このルールのおかげで、「ある変数を書き換えている最中に、別の場所でも同時に読み書きしてしまう」という不具合(データ競合)が、コンパイル時に防がれる。複数のスレッドが同じデータを同時に書き換えて壊してしまう、といった事故は、C言語では実行時まで発覚しないことが多いが、Rustではコンパイルが通った時点でそれが起こらないと保証される。
練習問題
&を使って、文字列の長さを返す関数str_lenを作り、渡した文字列がそのあとも使えることを確認してみよう。
解答例
fn main() {
let s = String::from("Rustは楽しい");
println!("長さ: {}", str_len(&s));
println!("元の文字列: {}", s); // 借用なので使える
}
fn str_len(text: &String) -> usize {
text.len()
}.len()はバイト数を返す点に注意(日本語の1文字は3バイト)。文字数を数えたい場合は.chars().count()を使う。
保存して次に進もう
cd /workspaces/rust-practice
git add .
git commit -m "Complete lesson 6: clone and borrowing"
git push体験が済んだら、exercises/06_borrowing/の練習問題にも挑戦してみよう(cargo runで一覧表示)。
次回予告
次回は、文字列や配列の一部分だけを借用するスライスについて学ぶ。これで所有権シリーズは完結となる。


コメント