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
блоке.