Каковы накладные расходы типа Option в Rust?

88

В Rust ссылки никогда не могут быть нулевыми, поэтому, если вам действительно нужен ноль, например связанный список, вы используете Optionтип:

struct Element {
    value: i32,
    next: Option<Box<Element>>,
}

Сколько накладных расходов связано с выделением памяти и действиями по разыменованию по сравнению с простым указателем? Есть ли какая-то «магия» в компиляторе / среде выполнения, позволяющая сделать это Optionбесплатно или менее затратно, чем если бы можно было реализовать Optionсамостоятельно в неосновной библиотеке с использованием той же enumконструкции или путем обертывания указателя в векторе?

Тило
источник

Ответы:

91

Да, есть некоторая магия компилятора, которая оптимизируется Option<ptr>до одного указателя (большую часть времени).

use std::mem::size_of;

macro_rules! show_size {
    (header) => (
        println!("{:<22} {:>4}    {}", "Type", "T", "Option<T>");
    );
    ($t:ty) => (
        println!("{:<22} {:4} {:4}", stringify!($t), size_of::<$t>(), size_of::<Option<$t>>())
    )
}

fn main() {
    show_size!(header);
    show_size!(i32);
    show_size!(&i32);
    show_size!(Box<i32>);
    show_size!(&[i32]);
    show_size!(Vec<i32>);
    show_size!(Result<(), Box<i32>>);
}

Распечатываются следующие размеры (на 64-битной машине, поэтому указатели составляют 8 байт):

// As of Rust 1.22.1
Type                      T    Option<T>
i32                       4    8
&i32                      8    8
Box<i32>                  8    8
&[i32]                   16   16
Vec<i32>                 24   24
Result<(), Box<i32>>      8   16

Обратите внимание , что &i32, Box, &[i32], Vec<i32>любое использование не-обнуляемым оптимизации указатель внутри Option!

гуон
источник
39
Более того, эта оптимизация происходит во всех « Option-подобных» перечислениях, поэтому она также будет работать для определенных пользователем Option.
Пол Стэнсифер
4
Также обратите внимание, что эту оптимизацию нельзя складывать. Это можно увидеть в последней строке примера. Когда вы указываете тип для Ok как (), этот конкретный тип результата становится «опцией, подобной перечислению» и, следовательно, не может быть оптимизирован на уровне опций. Но если вы попробуете, Result<i32, i32>то увидите, что оптимизация применяется снова.
Pajn
5
@Pajn Похоже, что, по крайней мере, по состоянию на март 2020 года, этот тип оптимизации может быть сложен, пока существует достаточно недопустимых двоичных представлений. Конечно, указатели, не допускающие значения NULL, обычно имеют только одно недопустимое двоичное представление.
Vaelus