пятница, 27 декабря 2019 г.

Владение ресурсами в Rust. Часть 4.

Продолжим рассматривать передачу владения по ссылке. Еще раз напомню, что физической передачи владения при этом не происходит, ни копирования, ни перемещения. В Rust это называется “одолжить” (borrowing). В обычном представлении одолжить значит сначала дать (lend) или взять (borrow) какую-то вещь, а потом ее вернуть и это неправильно. Более подходящее слово “разделить” (sharing). Например, когда у вас есть источник энергии и вы даете возможность подключиться к нему кому-то еще. Причем если вам захотелось его отключить, вам никто не может помешать это сделать, и энергия пропадет у того, кто к вам подключен в это время. Это известная проблема висячих ссылок. Чтобы этого избежать в Rust есть специальный инструмент borrow checker, который отвечает за проверку времени жизни ссылок, т. е. чтобы никто не отключил энергию в момент использования потребителями.

Если контролер ссылок не может понять время жизни каких-то ссылок, то выдаст ошибку на этапе компиляции, с просьбой указать это время явно. Тут нужно сказать, что проверка происходит всегда, но в простых ситуациях, контролер предполагает некоторое время жизни по умолчанию, и не требует указывать его. Например, при передаче одной ссылки в качестве параметра функции и одной в качестве возвращаемого значения, контролер предполагает, что время жизни принимаемой ссылки равно времени жизни возвращаемой. Если на вход передать две ссылки, то контролер потребует определить их время жизни. Время жизни указывается с помощью знака “ ’ ” с буквой. Сама по себе буква не является переменной, а служит именем канала для проверки. Разные ссылки могут быть “связаны” через один общий (‘a) канал или через несколько разных (‘a, ’b).

Понимание как это работает пришло ко мне, когда я представил связи в виде системы трубопроводов. Источником будут служить наши данные (переменные x и y), на которые мы хотим ссылаться. Потребителем будет переменная z, которая примет возвращаемое значение из функции. Указывая одинаковое время жизни для входных параметров и возвращаемого значения, мы определяем условие работы функции, при котором потребитель получит результат работы функции при условии, что источник в это время доступен.


 
Трубопровод проходит от источника к потребителю через тело функции, в которые передается и возвращается поток (значения, которые мы передаем по ссылке). Перебои водоснабжения — это ошибки, которые мы хотим избежать. Контролер ссылок прослеживает путь, и сообщает об ошибке если связь прервана, либо просит указать по какому каналу они будут связаны, если их несколько. Что может быть причиной разрыва канала? Например, перемещение владения ресурсами, на которые мы ссылаемся, после чего наши значения становятся не доступными по ссылке. Это может произойти, если внутри функции (или за ее пределами) мы передадим наши данные в другую функцию или попробуем вернуть локальную ссылку, созданную внутри функции, без передачи ей наших данных. Смотрим пример.

//
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

#[derive(Debug)]
struct Line<'a> {
    start: &'a Point,
    end: &'a Point,
}

//
//default lifetime
fn positive(value1: &i32) -> &i32 {
    if *value1 > 0 {
        value1
    } else {
        &0
    }
}

//borrowing parameters by refs - it is SHARING! (no move, no copy)
fn smaller<'a>(value1: &'a i32, value2: &'a i32) -> &'a i32 {
    if *value1 < *value2 {
        value1
    } else {
        value2
    }
}

fn main() {
    //default lifetime
    let n = -5;
    let pos_n = positive(&n);
    println!("{}", pos_n);

    //Sharing with function
    let x = 5;
    let y = 10;

    //lending to fn - it is SHARING! (no move, no copy)
    let z = smaller(&x, &y);
    println!("{}", z);

    //Sharing with struct
    let a = Point { x: 0, y: 0 };
    let b = Point { x: 1, y: 1 };

    //let d = a; //ERROR! move ownership from a to d

    //lending to struct - it is SHARING! (no move, no copy)
    let c = Line { start: &a, end: &b };
    println!("{:?}", c);
}

Перегуд В.

Комментариев нет:

Отправить комментарий