値の借用

前に見たように、関数を呼び出すときに所有権を移動する代わりに、 関数にその値を 借用 させることができます:

// 著作権 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

#[derive(Debug)]
struct Point(i32, i32);

fn add(p1: &Point, p2: &Point) -> Point {
    Point(p1.0 + p2.0, p1.1 + p2.1)
}

fn main() {
    let p1 = Point(3, 4);
    let p2 = Point(10, 20);
    let p3 = add(&p1, &p2);
    println!("{p1:?} + {p2:?} = {p3:?}");
}
  • add 関数は 2 つのポイントを 借用 し、新しいポイントを返します。
  • 呼び出し元は入力の所有権を保持します。

このスライドは 1 日目の参照に関する内容の復習であり、 関数引数と戻り値も少し含めるように内容を広げています。

さらに調べること

スタック上での戻り値とインライン化に関するメモ:

  • add からの値の返却が低コストであることを示してください。これは、コンパイラが add の呼び出しを main にインライン化することでコピー操作を削除できるためです。上の コードをスタックアドレスを表示するように変更し、Playground で実行するか、 Godbolt でアセンブリを見てください。“DEBUG” 最適化レベルではアドレスは変わるはずですが、“RELEASE” 設定に変更すると 同じままです:

    // 著作権 2023 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    #[derive(Debug)]
    struct Point(i32, i32);
    
    fn add(p1: &Point, p2: &Point) -> Point {
        let p = Point(p1.0 + p2.0, p1.1 + p2.1);
        println!("&p.0: {:p}", &p.0);
        p
    }
    
    pub fn main() {
        let p1 = Point(3, 4);
        let p2 = Point(10, 20);
        let p3 = add(&p1, &p2);
        println!("&p3.0: {:p}", &p3.0);
        println!("{p1:?} + {p2:?} = {p3:?}");
    }
  • Rust コンパイラは自動インライン化を行えます。これは関数単位で #[inline(never)] を使って無効にできます。

  • いったん無効にすると、表示されるアドレスはすべての最適化レベルで 変わります。Godbolt や Playground を見ると、この場合の値の返却は ABI に依存することがわかります。たとえば amd64 では、ポイントを構成する 2 つの i32 は 2 つのレジスタ(eax と edx)で返されます。