プログラミングにおける借用とは?|借用とは何か、具体例をまじえながら3分わかりやすく解説

【用語解説】プログラミング言語関連

こんちには。キノコードです。
このレッスンでは、 借用について説明をします。

▼ YouTube動画はこちらからどうぞ。

この記事の執筆・監修

キノコード
キノコード

テクノロジーアンドデザインカンパニー合同会社のCEO。
日本最大級のプログラミング教育のYouTubeチャンネル「キノコード」や、プログラミング学習サービス「キノクエスト」を運営。
著書「あなたの仕事が一瞬で片付くPythonによる自動化仕事術」や、雑誌「日経ソフトウエア」や「シェルスクリプトマガジン」への寄稿など実績多数。

イミュータブル・ミュータブルとは?

例えば、プログラミング言語のRustでは、メモリ管理について、所有権・借用・ライフタイムという独特な仕組みを採用しています。
そのため、開発する上で制約の多いOSやコンパイラなどのソフトウェアを開発することができますし、高速に動くプログラムを書くことができます。
この独特なメモリ管理の借用について説明します。
まず、借用の前に、変数におけるイミュータブル、ミュータブルについて説明します。
イミュータブルな変数は、変数を作ったあとに、変数の中身を変更できません。
逆にミュータブルな変数は、あとから変数の中身を変更できます。
これを前提知識に借用の説明をします。

借用とは

借用とは所有権を移さず、変数の中身を一定期間だけレンタルすることです。
借用は3つのルールがあります。
1つ目は、イミュータブルな借用なら複数できること。
2つ目は、ミュータブルな借用なら1つしかできないこと。
3つ目は、イミュータブルな借用とミュータブルな借用は同時にできないことです。
コードを書きながらみていきましょう。

イミュータブルな複数の借用とは

借用とは所有権を移さず、変数の中身を一定期間だけレンタルすることです。

Rustでは、イミュータブルな借用なら複数できます。つまり、変更不可な借用は何個でも作れます。
RustとJavaのコードを比較して、借用の特徴を見ていきましょう。
まずJavaのコードを書いてみます。

-Java-

public static void main(String[] args) {

    String a = new String(""hello"");

    String b = a;

    System.out.println(a);

    System.out.println(b);

}

このところで、変数bに変数aを代入しています。
コンパイルして実行してみましょう。

hello

hello

aにもbにも「hello」が入っているので、helloが2つ表示されました。

次に、Rustのコードを書いてみましょう。

main(){

    let a = "hello".to_string();

    let b = a; //★

    println!("{}", a);

    println!("{}", b);

所有権の動画で説明しましたが、何もしなければエラーになります。

error[E0382]: borrow of moved value: a"

イミュータブル(変更不可)な借用は変数の前に & を付けます。

fn main(){

    let a = "hello".to_string();

    let b = &a;

    println!("{}", a);

    println!("{}", b);

}

コンパイルして実行します。

hello

hello

helloが2つ表示されました。

所有権のムーブではなく、借用であればエラーになりません。
なぜなら、変数aに所有権が残っているからです。
これでJavaと同じ書き方ができました。

ミュータブルな1つだけの借用とは

ミュータブルな1つだけの借用をみていきましょう。

Rustの前に、Javaの場合をみてみましょう。
Javaの場合は、ミュータブルな借用は複数できます。

配列yと配列zを作成しています。
これらは両方ミュータブルな参照です。

-Java-

public static void main(String[] args) {

    int[] x = new int[3];

    int[] y = x; int[] z = x;

    for (int i = 0; i < 3; i++) {

        y[i] = i + 1;

       z[i] = i * 2;

    }

    for (int val : x) {

        System.out.print(val + " ");

    }

}

コンパイルして実行してみましょう。

0 2 4

yで変更した内容が、zの内容で上書きされてしまいました。
一方、Rustの場合は、ミュータブルな借用は、複数することができません。
コードを書いて試してみましょう。

fn main(){

    let mut x = vec![0, 0, 0];

    let y = &mut x;

    let z = &mut x;

    for i in 0..3 {

        y[i] = i + 1;

        z[i] = i * 2;

    }

    for var in x {

        print!("{}", var);

        print!("{}", " ");

    }

}

ミュータブルな借用の場合、変数の前に &そのあとにmut をつけます。

実行してみましょう。

cannot borrow x as mutable more than once at a time

(直訳:一度に複数回「x」を可変として借りることはできません。) 

Rustだと、ミュータブルな借用を複数作ろうとするとエラーになります。
一方、ミュータブルな借用を1つだけしてみます。

fn main(){

    let mut x = vec![0, 0, 0];

    let z = &mut x;

    for i in 0..3 {

        z[i] = i * 2;

    }

    for var in x {

        print!("{}", var);

        print!("{}", " ");

    }

} 

エラーにならずxの配列を表示させることができました。
Rustには、このような厳格なルールがあることので、Javaのような、あとからzでデータを上書きしてしまうような問題が起こりません。
最後のルールの「借用は同時にできない」のルールをみていきましょう。

このコードはyがミュータブルな借用、zがイミュータブルな借用をしようとしています。
コンパイルして実行してみます。

fn main(){

    let mut x = vec![0, 0, 0];

    let y = &mut x;

    let z = &x;

    for i in 0..3 {

        y[i] = i + 1;

    }

    for var in x {

        print!("{}", var);

        print!("{}", " ");

    }

エラーとなります。
エラーを訳すと、「xは可変としても借りられているので、不変として借りることができません」という意味です。
これが、ミュータブルな借用と、イミュータブルな借用は同時にできないというルールです。

KinoCode チャンネル

YouTubeで毎日動画配信しています。
動画は3分間なので、
 ・通勤時間
 ・お昼休み
 ・お手すきのとき
 ・寝る前
など手軽に視聴できます。
 
ちょっとしたインプットにどうぞ!
 
▼チャンネル登録はこちらからどうぞ。