Empréstimo (Borrowing)

Em vez de transferir a ownership ao chamar uma função, você pode permitir que uma função empreste o valor:

#[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:?}");
}
  • A função add pega emprestado (borrows) dois pontos e retorna um novo ponto.
  • O chamador mantém a ownership das entradas.

Notas sobre os retornos da pilha:

  • Demonstre que o retorno de somar é barato porque o compilador pode eliminar a operação de cópia. Modifique o código acima para imprimir endereços da pilha e execute-o no Playground ou veja o código assembly em Godbolt. No nível de otimização “DEBUG”, os endereços devem mudar, enquanto eles permanecem os mesmos quando a configuração é alterada para “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:?}");
    }
  • O compilador Rust pode fazer otimização de valor de retorno (Return Value Operation - RVO).

  • Em C++, a elisão (omissão) de cópia deve ser definida na especificação da linguagem porque os construtores podem ter efeitos colaterais. Em Rust, isso não é um problema. Se o RVO não aconteceu, o Rust sempre executará uma cópia memcpy simples e eficiente.