
Unique
Unique<T> - это тип указателя в Rust, который обычно используется внутри Rust для представления уникальной семантики владения и полезен для построения абстракций, таких как Box<T>, Vec<T>, String и HashMap<K, V>.
В стандартной библиотеке объявляется в core/src/ptr/unique.rs как:
pub struct Unique<T: ?Sized> {
pointer: NonNull<T>,
_marker: PhantomData<T>,
}
Unique<T> - это ненулевой ковариантный уникальный указатель. Вот что означают эти термины:
- Это гарантированно никогда не будет NULL.
- Ковариантный: это свойство, которое позволяет использовать его с подтипами и супертипами. В Rust ковариация
Unique<T>используется, например, для обеспечения ковариантностиBox<T>сT. - Уникальный: означает, что есть только один владелец.
Такое сочетание свойств позволяет Rust делать определенные допущения, позволяющие оптимизировать код. Например, поскольку указатели Unique<T> не имеют значения NULL и уникальны, оптимизатор Rust может предположить, что переопределение Unique<T> всегда безопасно и что указатель не будет изменен или перемещен какой-либо другой частью кода.
Обратите внимание, что Unique<T> является внутренней детализацией реализации стандартной библиотеки Rust. Это не тип, который обычно используется непосредственно в собственном коде. Вместо этого можно использовать такие типы, как Box<T>, Vec<T> и т.д., которые создаются с использованием Unique за кулисами.
Например, Box<T> объявляется в стандартной библиотеке в файле alloc/src/boxed.rs как:
pub struct Box<
T: ?Sized,
#[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
>(Unique<T>, A);
PhantomData
Как показано выше, Unique<T> имеет поле _marker типа PhantomData<T>.
PhantomData<T> в Rust - этот тип используется, чтобы указать, что дженерик тип T - часть структуры данных, даже если T не используется в структуре данных.
PhantomData<T> играет важную роль в способности компилятора Rust понимать владение, заимствование и время жизни, и это важно при создании сложных структур данных или при работе с необработанными указателями.
Теперь Unique<T> обычно используется с PhantomData, поскольку сам Unique<T> не «владеет» своим указателем в системе типов Rust. Фактическое владение часто выражается с помощью PhantomData.
В объявлении Unique<T> структуры PhantomData<T> выражает, что он «владеет» T, даже если T фактически не используется в структуре. Это говорит компилятору Rust, что при удалении Unique<T> он может удалить T, поэтому он не должен позволять использовать T впоследствии.
Без PhantomData<T> система типов Rust не поймет, что Unique<T> владеет T, и это потенциально может привести к неопределенному поведению.
Таким образом, PhantomData<T> и Unique<T> часто используются вместе в Rust для выражения сложных отношений владения, которые система типов Rust не может выразить самостоятельно.
NonNull
Кроме того, Unique<T> не хранит необработанный указатель напрямую. Вместо этого он является указателем поля типа NonNull<T>.
В Rust значение NonNull<T> является оберткой необработанного указателя, которое гарантированно не равно NULL. Это по существу то же самое, что и *mut T, но с добавленным инвариантом, что он не является нулевым.
Необработанные указатели в Rust обычно используются при взаимодействии с кодом C или при построении небезопасных абстракций Rust. Однако указатели NULL часто могут вызывать ошибки и сбои, поэтому NonNull<T> предоставляется как способ гарантировать, что необработанный указатель никогда не будет null.
Это дает некоторые гарантии безопасности в том смысле, что перед использованием не нужно проверять значение NULL. Но имейте в виду, что, несмотря на то, что оно не может быть нулевым, использование NonNull<T> по-прежнему небезопасно, поскольку оно не дает вам никаких гарантий заимствования или срока службы, которые ссылаются (&T и &mut T).
Ниже приведен пример использования NonNull<T>:
use std::ptr::NonNull;
let mut value = 10;
let pointer = NonNull::new(&mut value as *mut _);if let Some(ptr) = pointer {
unsafe {
*ptr.as_ptr() = 20;
}
assert_eq!(value, 20);
}
В этом примере NonNull::new используется для создания нового NonNull<T>. Затем NonNull::as_ptr используется для получения необработанного указателя, который можно отменить и изменить в unsafe блоке.