グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



安全性、速度、並行性を兼ね備えた言語と、巷でうわさの「Rust」を覗いてみる(その6:参照と借用)

みなさまごきげんよう。

今回も「The Rust Programming Language」を読みながら、
参照と借用についてみていきたいとおもいます。

これまでの復習

教科書の「Listing 4-5」のコードを復習をかねて見ていきます。

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() returns the length of a String

    (s, length)
}
 calculate_length 関数は、引数にString型の s をとり、その長さを返します。

注目すべきは、main関数側の変数 s1 が、 calculate_length 関数の引数 s にmoveされるので、消費されてしまいます。
なので、関数の戻り値をタプルにして、引数 s とサイズを返してします。

こうすることで、s1 > s > s2 と所有権が移動するので、 s1 を利用したい場合に、同じ内容の s2 を利用できます。

しかし、 s1 を再利用したいために、引数で返すのは面倒です。
そこで参照を利用します。

参照を利用する

実引数を &s1 にすることで s1 の参照を作り、仮引数を &String にすることで、その参照を利用できます。
こうすることで、 s1 の所有権が消費されることなく、参照として関数に渡せるようです。

 calculate_length 関数の戻り値は、 s1 をそのまま再利用できるようになったので、 usize だけにできます。

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}
記述量が減り、シンプルになりました。

 calculate_length 関数の引数 s は、参照なので所有権がありません。
なので、 s がスコープを抜けても、 s が指し示している s1 がドロップされることはありません。

ここで言葉の整理をしておきます。

We call the action of creating a reference borrowing. As in real life, if a person owns something, you can borrow it from them. When you’re done, you have to give it back. You don’t own it.The Rust Programming Language

上記の引用によると、参照を作る行為を借用(borrowing)と呼ぶようです。

借用した値を変更する

参照した値を変更するとどうなるのか試す前に、参照を使わない場合の実装を確認してみます。

fn main() {
    let s = String::from("hello");

    let ss = change(s);
    
    println!("{}", ss);
}

fn change(mut some_string: String) -> String {
    some_string.push_str(", world");
    some_string
}
 change 関数の引数 some_string に、 mut をつけているところがポイントです。 変数はデフォルト状態では不変なので、 mut をつけて可変にする必要があります。

ここまで確認した上で、参照の場合を見ていきます。

fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}
実行すると以下のエラーが出力されました。

error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference
 --> src/main.rs:8:5
  |
7 | fn change(some_string: &String) {
  |                        ------- help: consider changing this to be a mutable reference: `&mut String`
8 |     some_string.push_str(", world");
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

For more information about this error, try `rustc --explain E0596`.
参照の some_string もデフォルトでは mutable になっていることが分かります。

Just as variables are immutable by default, so are references. We’re not allowed to modify something we have a reference to.The Rust Programming Language

変数と同様に参照もデフォルトでは不変になっていると書かれています。

では、次に可変の参照で試してみます。

 &mut  s の参照を作り、 change 関数の引数を &mut に変更します。

fn main() {
    let mut s = String::from("hello");

    change(&mut s);
    println!("{}", s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
このコードだと問題なく動作します。

可変の参照は、 &mut で生成し、それを受け取る関数側も、 &mut を付けると可変の参照をやり取りすることができます。

次回は、可変の参照の制約について見ていきたいと思います。

この記事を書いた人

tkr2f
tkr2f事業開発部 web application engineer
2008年にアーティスへ入社。
システムエンジニアとして、SI案件のシステム開発に携わる。
その後、事業開発部の立ち上げから自社サービスの開発、保守をメインに従事。
ドメイン駆動設計(DDD)を中心にドメインを重視しながら、保守可能なソフトウェア開発を探求している。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします