0

В чем разница между передачей значения функции по ссылке и передачей через Box?

56

В чем разница между передачей значения функции по ссылке и передачей "по коробке"?

В приведенном коде рассматривается вопрос о разнице между передачей переменной, хранящейся в стеке, и переменной, хранящейся в куче, в функции. Программа выглядит так:

fn main() {
    let mut stack_a = 3;
    let mut heap_a = Box::new(3);

    foo(&mut stack_a);
    println!("{}", stack_a);

    let r = foo2(&mut stack_a);
    // ошибка компиляции, если раскомментировать следующую строку
    // println!("{}", stack_a);

    bar(heap_a);
    // ошибка компиляции, если раскомментировать следующую строку
    // println!("{}", heap_a);
}

fn foo(x: &mut i32) {
    *x = 5;
}

fn foo2(x: &mut i32) -> &mut i32 {
    *x = 5;
    x
}

fn bar(mut x: Box<i32>) {
    *x = 5;
}

Вопрос заключается в следующем: Почему heap_a перемещается в функцию, тогда как stack_a нет (переменная stack_a все еще доступна в операторе println! после вызова foo())?

При попытке раскомментировать строку println!("{}", stack_a); возникает следующая ошибка:

error[E0502]: cannot borrow `stack_a` as immutable because it is also borrowed as mutable
  --> src/main.rs:10:20
   |
8  |     let r = foo2(&mut stack_a);
   |                       ------- mutable borrow occurs here
9  |     // compile error if the next line is uncommented
10 |     println!("{}", stack_a);
   |                    ^^^^^^^ immutable borrow occurs here
...
15 | }
   | - mutable borrow ends here

Я полагаю, что эта ошибка может быть объяснена с помощью концепции времени жизни. В случае функции foo, переменная stack_a (в функции main) передается в функцию foo, но компилятор определяет, что время жизни аргумента функции foo, x: &mut i32, заканчивается в конце функции foo. Поэтому компилятор позволяет использовать переменную stack_a в функции main после возврата из foo. В случае функции foo2, stack_a также передается в функцию, но мы также возвращаем его.

Почему же время жизни heap_a не заканчивается в конце функции bar?

1 ответ(ов)

0

Разница между передачей по ссылке и "по коробке" заключается в том, что в случае передачи по ссылке (то есть "взять взаймы") вызывающая сторона отвечает за освобождение объекта, тогда как в случае передачи по коробке (то есть "передать") вызываемая сторона отвечает за освобождение объекта.

Поэтому Box<T> полезен для передачи объектов с ответственностью за освобождение, в то время как ссылка полезна для передачи объектов без ответственности за освобождение.

Вот простой пример, который демонстрирует эти идеи:

fn main() {
    let mut heap_a = Box::new(3);
    foo(&mut *heap_a);
    println!("{}", heap_a);

    let heap_b = Box::new(3);
    bar(heap_b);
    // нельзя использовать `heap_b`. `heap_b` был освобожден в конце `bar`
    // println!("{}", heap_b);
} // `heap_a` уничтожается здесь

fn foo(x: &mut i32) {
    *x = 5;
}

fn bar(mut x: Box<i32>) {
    *x = 5;
} // heap_b (теперь `x`) освобождается здесь

В этом примере мы видим, как передача по ссылке позволяет продолжать использовать объект после вызова функции foo, в то время как передача через Box в функции bar приводит к освобождению объекта, и далее его использование становится невозможным.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь