2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[初学者向け]Rustを触ってみよう⑤ ~参照と借用編~

Last updated at Posted at 2025-01-04

はじめに

こんにちは!ITスクールRareTECHにてCS(Customer Support)を担当している池村です。今回の記事はRustの参照と借用についてとなります。借用はRust独特の考え方ですかね。前回の所有権の話と繋がっているので、まずはそちらを学習してからをオススメします。

今回の記事はRustの『The Book』にある内容を自分なりに噛み砕いて記事にしたものです。

c8d395eb-cb20-4386-97b8-a4bbe22c1f63_720.png

参照と借用

早速本題ですが、所有権が違うデータを所有権をムーブさせることなく渡すことができる機能のことを参照と借用と言います。

書き方

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()
}

上記のコードは、String型の変数の文字列の長さを測るための関数を準備して実際に動かしています。
calculate_length関数にある引数のsは、s1を参照するための変数となります。あくまで参照しているだけなので、s1が持っている所有権がムーブしているわけではありません。要は&(アンバサンド)をつけることで、参照させることができるようになるんですね。

trpl04-05.png

ここが大事だと思う部分ですが。

参照がスコープを抜けても値はドロップされない!

所有権をもらわないということは、所有権を返すようなこともなくなるんですね。

この関数の引数に参照を取ることを借用と言います。

借用したものの取り扱い

借用したデータに追加で文字列を加えてみます。

文字列の追加
fn main() {
    let s = String::from("Hello");
    let len = test_length(&s);

    print!("{}の文字数は {}文字です", s, len);
}

fn test_length(s: &String) -> usize {
    s.push_str(", World");
    s.len()
}

これはエラーになるようですね。

エラー文
error[E0596]: cannot borrow `*s` as mutable, as it is behind a `&` reference
 --> src/main.rs:9:5
  |
9 |     s.push_str(", World");
  |     ^ `s` is a `&` reference, so the data it refers to cannot be borrowed as mutable
  |
help: consider changing this to be a mutable reference
  |
8 | fn test_length(s: &mut String) -> usize {
  |                    +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `Rust_Training` (bin "Rust_Training") due to 1 previous error

日本語にしてみましょう。

エラー文(日本語)
 --> src/main.rs:9:5
  |
9 |     s.push_str(", World");
  |     ^ `s``&`参照なので、参照しているデータを可変として借用することはできません

だそうです。
変数がそもそも不変なので、参照先でも不変ということですね。

じゃあmutをつければ可変にできるのか?->結論:できます。

書き方
fn main() {
    let mut s = String::from("Hello");
    let len = test_length(&mut s);

    print!("{}の文字数は {}文字です", s, len);
}

fn test_length(s: &mut String) -> usize {
    s.push_str(", World");
    s.len()
}

書き方として気をつけたいのは、引数に渡す時の書き方と、引数定義の部分の書き方ですね。

  • test_length(&mut s)
  • fn test_length(s: &mut String)

&mutという修飾子で差別化しているということがわかりました。

注意点として、可変な参照は1回しか持てないという部分。(特定のスコープ内)

可変な参照は1度のみ

可変な参照の回数制限について
    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
(エラー: 一度に`s`を可変として2回以上借用することはできません)
 --> src/main.rs:5:14
  |
4 |     let r1 = &mut s;
  |              ------ first mutable borrow occurs here
  |                    (最初の可変な参照はここ)
5 |     let r2 = &mut s;
  |              ^^^^^^ second mutable borrow occurs here
  |                    (二つ目の可変な参照はここ)
6 |
7 |     println!("{}, {}", r1, r2);
  |                        -- first borrow later used here

ここは他の言語とかなり違うところですね。データ競合を防ぐための仕組みになっているようです。

これならOK👇

{}を使う
let mut s = String::from("hello");

{
    let r1 = &mut s;

} // r1はここでスコープを抜けるので、問題なく新しい参照を作ることができる

let r2 = &mut s;

不変で借用したもの

不変で借用したものを可変にすると?
let mut s = String::from("hello");

let r1 = &s; // 問題なし
let r2 = &s; // 問題なし
let r3 = &mut s; // 大問題!
エラー文
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as
immutable
(エラー: `s`は不変で借用されているので、可変で借用できません)
 --> borrow_thrice.rs:6:19

複数の不変参照を作ることは許可されているようです。

ダングリング参照について

ダングリングとは、無効なメモリアドレスを指しているポインタや参照のことを言います。データ自体がなくなっているのに、その参照やポインタが残っていたりすることですね。エラー要因としてかなり大きな問題ですね。

例えばこんな形
fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");

    &s
}

これは参照だけを返していますが、関数のスコープ内から出た時点で変数sのデータはドロップされるわけですから、何もないところを指すデータを返しています。
これは&を使わず、直接sを返すことで所有権がムーブされることで解決します。

法則として

  • 任意のタイミングで、一つの可変参照不変な参照いくつでものどちらかを行える
  • 参照は常に有効でなければならない

この辺りは実際に色々作ってみた時に出てくる問題な気がしているので、ゴリゴリ書いてエラーとぶつかって慣れるしかなさそうです。

おわりに

今回は所有権の続きで参照と借用についてまとめました。
2025年年明け一つ目の記事でしたが、割とすんなり理解できた気がします。
元々Rustを触り始めたのはこれらの概念が面白そうと思ったからですが、実際に概念理解し始めると楽しいですね。基礎的なところは程々に、あとは実際にアプリでも作ってみたいと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?