% Содержимое структуры
Для начала необходимо разобраться с компоновкой структуры. Vec состоит из трех частей: указатель на место в памяти, размер места в памяти и количество инициализированных элементов.
Наивно полагаем, что нам нужен такой дизайн:
pub struct Vec<T> { ptr: *mut T, cap: usize, len: usize, } fn main() {}
И в самом деле это скомпилируется. К сожалению, дизайн неправильный. Во-
первых, компилятор даст нам слишком строгую вариантность. Поэтому &Vec<&'static str>
нельзя будет использовать, где ожидается &Vec<&'a str>
. Что более важно,
он даст некорректную информацию о владении анализатору сброса, потому что
компилятор будет думать, что мы не хотим владеть никакими значениями типа T
.
Смотри главу по владениям и временам жизни для всех деталей
вариантности и проверки сброса.
Как мы уже видели в главе о владении, следует использовать Unique<T>
вместо
*mut T
, если у нас есть сырой указатель на место в памяти, которым мы владеем.
Хотя Unique нестабилен, поэтому нам бы не следовало его использовать.
Поясним, Unique - это обертка вокруг сырого указателя, которая:
- вариантна над
T
- может владеть значением типа
T
(для проверки сброса) - является Send/Sync, если
T
- Send/Sync - разыменуется в
*mut T
(поэтому она действует как*mut
в нашем коде) - Наш указатель никогда не будет нулевым (поэтому
Option<Vec<T>>
оптимизирован по нулевому указателю)
Мы можем реализовать все эти требования кроме последнего в стабильном Rust:
use std::marker::PhantomData; use std::ops::Deref; use std::mem; struct Unique<T> { ptr: *const T, // *const для вариантности _marker: PhantomData<T>, // Для анализатора сброса } // Выведение Send и Sync безопасно, потому что мы уникальные владельцы // данных. В этом случае Unique<T> это "просто" T. unsafe impl<T: Send> Send for Unique<T> {} unsafe impl<T: Sync> Sync for Unique<T> {} impl<T> Unique<T> { pub fn new(ptr: *mut T) -> Self { Unique { ptr: ptr, _marker: PhantomData } } } impl<T> Deref for Unique<T> { type Target = *mut T; fn deref(&self) -> &*mut T { // Нет явного приведения *const к *mut, с одновременным получением // ссылки. Поэтому мы просто используем // трансмутацию, ведь это все "просто указатели". unsafe { mem::transmute(&self.ptr) } } } fn main() {}
К сожалению, механизмы, позволяющие утверждать, что ваше значение отличается от нуля, нестабильны и вряд ли будут стабилизированы в ближайшее время. Поэтому, ладно, примем удар и используем Unique из стандартной библиотеки:
#![feature(unique)] use std::ptr::{Unique, self}; pub struct Vec<T> { ptr: Unique<T>, cap: usize, len: usize, } fn main() {}
Если вы не волнуетесь за оптимизацию нулевого указателя, можете использовать
стабильный код. Однако мы будет проектировать остальной код, обладая этой
оптимизацией. В частности, Unique::new
вызывать небезопасно, потому что, если
передавать null
, то получишь Неопределенное Поведение. Нашему стабильному
Unique не нужен небезопасный new
, потому что он не дает никаких интересных
гарантий своего содержимого.