قرض‌گیری یک مقدار

همان‌طور که پیش‌تر دیدیم، به جای انتقال مالکیت هنگام فراخوانی یک تابع، می‌توانید به تابع اجازه دهید که ارجاعی به مقدار داشته باشد:

#[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 ارجاع می‌گیرد به دو نقطه و یک نقطه جدید برمی‌گرداند.
  • فراخوانی‌کننده مالکیت ورودی‌ها را حفظ می‌کند.
This slide should take about 10 minutes.

این اسلاید مرور مطالب مربوط به ارجاعات از روز اول است که به طور جزئی به شامل آرگومان‌های تابع و مقادیر بازگشتی گسترش یافته است.

برای کاوش بیشتر

یادداشت‌هایی در مورد بازگشت‌های stack و درون‌ریزی (inlining):

  • برای نشان دادن این که بازگشت از add ارزان است، زیرا کامپایلر می‌تواند عملیات کپی را حذف کند، با درون‌ریزی (inlining) فراخوانی add به تابع main، کد فوق را تغییر دهید تا آدرس‌های پشته چاپ شوند و آن را در Playground اجرا کنید یا به اسمبلی در Godbolt نگاه کنید. در سطح بهینه‌سازی "DEBUG"، آدرس‌ها باید تغییر کنند، در حالی که با تغییر به تنظیم "RELEASE" ثابت می‌مانند:

    #[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 می‌تواند به‌طور خودکار درون‌ریزی (inlining) انجام دهد، که می‌تواند در سطح تابع با [inline(never)]# غیرفعال شود.

  • پس از غیرفعال کردن درون‌ریزی، آدرس‌های چاپ شده در تمامی سطوح بهینه‌سازی تغییر خواهند کرد. با نگاه کردن به Godbolt یا Playground، می‌توان دید که در این حالت، بازگشت مقدار به ABI بستگی دارد، به عنوان مثال در amd64، دو i32 که نقطه را تشکیل می‌دهند، در دو رجیستر (مانند eax و edx) بازگردانده خواهند شد.