Jaki jest narzut typu Option Rusta?

85

W Rust referencje nigdy nie mogą być zerowe, więc w przypadku, gdy faktycznie potrzebujesz null, na przykład listy połączonej, użyj Optiontypu:

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

Ile narzutu wiąże się z tym, jeśli chodzi o alokację pamięci i kroki do wyłuskiwania w porównaniu do prostego wskaźnika? Czy w kompilatorze / środowisku uruchomieniowym jest jakaś „magia”, aby uczynić ją Optionbezkosztową lub mniej kosztowną, niż gdyby ktoś miał zaimplementować Optionją samodzielnie w bibliotece innej niż podstawowa przy użyciu tej samej enumkonstrukcji lub zawijając wskaźnik w wektorze?

Thilo
źródło

Odpowiedzi:

88

Tak, istnieje pewna magia kompilatora, która optymalizuje się Option<ptr>do pojedynczego wskaźnika (przez większość czasu).

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>>);
}

Drukowane są następujące rozmiary (na komputerze 64-bitowym, więc wskaźniki mają 8 bajtów):

// 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

Należy zauważyć, że &i32, Box, &[i32], Vec<i32>wszystko użycie non-pustych optymalizacja wskaźnik wewnątrz Option!

huon
źródło
38
Co więcej, ta optymalizacja występuje we wszystkich Optionwyliczeniach typu „ -”, więc będzie działać również dla wyliczeń zdefiniowanych przez użytkownika Option.
Paul Stansifer
4
Należy również pamiętać, że tej optymalizacji nie można łączyć. Można to zobaczyć w ostatnim wierszu przykładu. Po określeniu typu dla Ok as () ten konkretny typ wyniku staje się „opcją, taką jak wyliczenie” i dlatego nie może być zoptymalizowany na poziomie opcji. Ale jeśli spróbujesz z Result<i32, i32>, zobaczysz, że optymalizacja zostanie zastosowana ponownie.
Pajn
5
@Pajn Wygląda na to, że przynajmniej od marca 2020 r. Ten typ optymalizacji można łączyć, o ile jest wystarczająco dużo nieprawidłowych reprezentacji binarnych. Oczywiście wskaźniki niedopuszczające wartości null zwykle mają tylko jedną nieprawidłową reprezentację binarną.
Vaelus