Как сопоставить шаблон со значениями внутри типа, реализующего Deref, например Box, без копирования содержимого? - PullRequest
0 голосов
/ 03 ноября 2018

У меня есть данные, содержащиеся в Box, и я хотел бы сопоставить их с шаблоном без случайного копирования содержимого Box из кучи в стек; как мне это сделать?

Давайте предположим следующий код:

enum SomeEnum {
    SomeEntry,
    AnotherEntry,
}

fn main() {
    let boxed_value = Box::new(SomeEnum::AnotherEntry);

    match *boxed_value {
        SomeEnum::SomeEntry => {}
        SomeEnum::AnotherEntry => {}
    }
}

Копирует ли это перечисление из поля в стек и совпадение с образцом в этой копии, или же выполняется сопоставление непосредственно со значением, указанным в блоке?

А как насчет этого варианта?

use std::ops::Deref;

enum SomeEnum {
    SomeEntry,
    AnotherEntry,
}

fn main() {
    let boxed_value = Box::new(SomeEnum::AnotherEntry);

    match boxed_value.deref() {
        SomeEnum::SomeEntry => {}
        SomeEnum::AnotherEntry => {}
    }
}

Кажется, что простая разыменование блока не создает автоматически копию, иначе невозможно было бы создать ссылку на содержащееся в нем значение с помощью let x = &*boxed_value. Это приводит к вопросу об этом синтаксисе:

enum SomeEnum {
    SomeEntry,
    AnotherEntry,
}

fn main() {
    let boxed_value = Box::new(SomeEnum::AnotherEntry);

    match &*boxed_value {
        SomeEnum::SomeEntry => {}
        SomeEnum::AnotherEntry => {}
    }
}

1 Ответ

0 голосов
/ 03 ноября 2018

Первый : в Rust нет неявных дорогостоящих копий, в отличие, например, от C ++. В то время как в C ++ действием по умолчанию является «глубокое копирование» (через конструктор копирования или подобное), действие по умолчанию в Rust движется. Перемещение - это мелкая копия, которая (а) обычно очень мала и дешева и (б) может быть удалена оптимизатором в большинстве случаев. Чтобы получить глубокие клоны в Rust, вы должны вручную использовать .clone(). Если вы этого не сделаете, , вам обычно не нужно беспокоиться об этом

Секунда : при сопоставлении с перечислением рассматривается только дискриминант этого перечисления (если вы не связываете поля перечисления, см. Ниже). Это тег или метаданные, которые указывают, какой вариант перечисления хранится в значении. Этот тег крошечный: он вписывается в 8 бит почти во всех случаях (перечисления с более чем 256 вариантами встречаются редко). Так что вам не нужно беспокоиться об этом. И в вашем случае у нас есть C-подобные перечисления без каких-либо полей. Таким образом, enum хранит только тег и, следовательно, тоже крошечный.

Так что насчет полей enum, которые могут быть дорогостоящими для копирования? Как это:

enum SomeEnum {
    SomeEntry(String),
    AnotherEntry,
}

let boxed_value = Box::new(SomeEnum::AnotherEntry);

match *boxed_value {
    SomeEnum::SomeEntry(s) => drop::<String>(s), // make sure we own the string
    SomeEnum::AnotherEntry => {},
}

Так что в этом случае один вариант хранит String. Поскольку глубокое копирование строки довольно дорого, Rust не будет делать это неявно. В матче мы пытаемся сбросить s и утверждать, что это String. Это означает, что мы (то есть: тело совпадающей руки) владеем строкой. Таким образом, когда спичечное устройство владеет им, но мы не клонировали его, это означает, что внешняя функция больше не может им владеть. И на самом деле, если вы попытаетесь использовать boxed_value после совпадения, вы получите ошибки перемещения от компилятора. Опять же, либо вы получите ошибку компилятора, либо автоматически не произойдет ничего плохого.

Кроме того, вы можете написать SomeEnum::SomeEntry(ref s) в match. В этом случае строка связана ссылкой s (поэтому вызов drop() больше не будет работать). В этом случае мы никогда не переходим от boxed_value. Это то, что я называю «отложенным перемещением», но я не уверен, что это официальный термин для этого. Но это просто означает: при сопоставлении с образцом входное значение вообще не перемещается, пока из него не сместится привязка в шаблоне.

И, наконец, посмотрите этот код и сгенерированную сборку . Сборка оптимальна. Итак, еще раз: хотя вы можете беспокоиться о случайных клонах, когда вы пришли из мира C ++, на самом деле это не то, о чем вам нужно беспокоиться в Rust.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...