Примитивный тип pointer
Сырые указатели (raw pointers) *const T и *mut T
Смотрите также модуль std::ptr.
Работа с сырыми указателями в Rust встречается редко и обычно ограничена несколькими паттернами. Сырые указатели могут быть:
- Выходящими за границы (
out-of-bounds) - Невыровненными (
unaligned) - Нулевыми (
null)
Однако при загрузке из или записи в сырой указатель он должен быть валидным для данного доступа и выровненным. При использовании выражений доступа к полю, индексации кортежа, массива или среза на сыром указателе применяются правила арифметики указателей в пределах границ.
Запись через сырой указатель с помощью *ptr = data вызывает drop на старом значении, поэтому если тип имеет деструктор (drop glue) и память ещё не инициализирована, необходимо использовать write - иначе drop будет вызван на неинициализированной памяти.
Используйте функции null и null_mut для создания нулевых указателей, а метод is_null типов *const T и *mut T для проверки на null. Типы *const T и *mut T также определяют метод offset для арифметики указателей.
Распространённые способы создания сырых указателей
1. Приведение ссылки (&T) или изменяемой ссылки (&mut T)
#![allow(unused)] fn main() { let my_num: i32 = 10; let my_num_ptr: *const i32 = &my_num; let mut my_speed: i32 = 88; let my_speed_ptr: *mut i32 = &mut my_speed; }
Чтобы получить указатель на упакованное значение (Box), разыменуйте box:
#![allow(unused)] fn main() { let my_num: Box<i32> = Box::new(10); let my_num_ptr: *const i32 = &*my_num; let mut my_speed: Box<i32> = Box::new(88); let my_speed_ptr: *mut i32 = &mut *my_speed; }
Это не забирает владение исходным выделением памяти и не требует управления ресурсами позже, но нельзя использовать указатель после окончания его времени жизни.
2. Потребление box'а (Box<T>)
Функция into_raw потребляет box и возвращает сырой указатель. Она не уничтожает T и не освобождает память.
#![allow(unused)] fn main() { let my_speed: Box<i32> = Box::new(88); let my_speed: *mut i32 = Box::into_raw(my_speed); // Забрав владение исходным `Box<T>`, мы обязаны позже собрать его для уничтожения unsafe { drop(Box::from_raw(my_speed)); } }
Примечание: вызов drop здесь для ясности - он указывает, что мы закончили работу с данным значением и его следует уничтожить.
3. Создание с помощью &raw
Вместо приведения ссылки к сырому указателю можно использовать операторы сырого заимствования &raw const (для *const T) и &raw mut (для *mut T). Эти операторы позволяют создавать сырые указатели на поля, на которые нельзя создать ссылку (без возникновения неопределённого поведения), например, на невыровненное поле. Это может быть необходимо при работе с упакованными структурами (packed structs) или неинициализированной памятью.
#![allow(unused)] fn main() { #[derive(Debug, Default, Copy, Clone)] #[repr(C, packed)] struct S { aligned: u8, unaligned: u32, } let s = S::default(); let p = &raw const s.unaligned; // невозможно с приведением }
4. Получение из C
#![allow(unused)] fn main() { #[allow(unused_extern_crates)] extern crate libc; unsafe { let my_num: *mut i32 = libc::malloc(size_of::<i32>()) as *mut i32; if my_num.is_null() { panic!("failed to allocate memory"); } libc::free(my_num as *mut core::ffi::c_void); } }
Обычно вы не будете буквально использовать malloc и free из Rust, но C API часто возвращают указатели, поэтому они являются распространённым источником сырых указателей в Rust.
Реализации методов
Методы для *const T
| Метод | Версия | Описание |
|---|---|---|
is_null(self) -> bool | 1.0.0 (const: 1.84.0) | Возвращает true, если указатель нулевой |
cast<U>(self) -> *const U | 1.38.0 (const: 1.38.0) | Приводит к указателю другого типа |
try_cast_aligned<U>(self) -> Option<*const U> | nightly | Пытается привести к указателю другого типа с проверкой выравнивания |
with_metadata_of<U>(self, meta: *const U) -> *const U | nightly | Использует адресное значение в новом указателе другого типа |
cast_mut(self) -> *mut T | 1.65.0 (const: 1.65.0) | Изменяет константность без изменения типа |
addr(self) -> usize | 1.84.0 | Получает "адресную" часть указателя |
expose_provenance(self) -> usize | 1.84.0 | Раскрывает "происхождение" указателя для будущего использования |
with_addr(self, addr: usize) -> *const T | 1.84.0 | Создаёт новый указатель с заданным адресом и происхождением self |
map_addr(self, f: FnOnce(usize) -> usize) -> *const T | 1.84.0 | Создаёт новый указатель, отображая адрес self на новый |
to_raw_parts(self) -> (*const (), Metadata) | nightly | Разбирает (возможно, широкий) указатель на компоненты |
as_ref<'a>(self) -> Option<&'a T> | 1.9.0 (const: 1.84.0) | Возвращает None, если указатель нулевой, иначе ссылку на значение |
as_ref_unchecked<'a>(self) -> &'a T | nightly | Возвращает ссылку на значение за указателем без проверки на null |
as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>> | nightly | Аналогично as_ref, но допускает неинициализированные значения |
offset(self, count: isize) -> *const T | 1.0.0 (const: 1.61.0) | Добавляет знаковое смещение к указателю |
byte_offset(self, count: isize) -> *const T | 1.75.0 (const: 1.75.0) | Добавляет знаковое смещение в байтах |
wrapping_offset(self, count: isize) -> *const T | 1.16.0 (const: 1.61.0) | Добавляет знаковое смещение с оборачивающей арифметикой |
wrapping_byte_offset(self, count: isize) -> *const T | 1.75.0 (const: 1.75.0) | Добавляет знаковое смещение в байтах с оборачиванием |
mask(self, mask: usize) -> *const T | nightly | Маскирует биты указателя согласно маске |
offset_from(self, origin: *const T) -> isize | 1.47.0 (const: 1.65.0) | Вычисляет расстояние между двумя указателями |
byte_offset_from<U>(self, origin: *const U) -> isize | 1.75.0 (const: 1.75.0) | Вычисляет расстояние в байтах между указателями |
offset_from_unsigned(self, origin: *const T) -> usize | 1.87.0 (const: 1.87.0) | Вычисляет расстояние между указателями, где self >= origin |
byte_offset_from_unsigned<U>(self, origin: *const U) -> usize | 1.87.0 (const: 1.87.0) | Вычисляет расстояние в байтах, где self >= origin |
guaranteed_eq(self, other: *const T) -> Option<bool> | nightly | Проверяет, гарантированно ли равны указатели |
guaranteed_ne(self, other: *const T) -> Option<bool> | nightly | Проверяет, гарантированно ли не равны указатели |
add(self, count: usize) -> *const T | 1.26.0 (const: 1.61.0) | Добавляет беззнаковое смещение к указателю |
byte_add(self, count: usize) -> *const T | 1.75.0 (const: 1.75.0) | Добавляет беззнаковое смещение в байтах |
sub(self, count: usize) -> *const T | 1.26.0 (const: 1.61.0) | Вычитает беззнаковое смещение из указателя |
byte_sub(self, count: usize) -> *const T | 1.75.0 (const: 1.75.0) | Вычитает беззнаковое смещение в байтах |
wrapping_add(self, count: usize) -> *const T | 1.26.0 (const: 1.61.0) | Добавляет беззнаковое смещение с оборачиванием |
wrapping_byte_add(self, count: usize) -> *const T | 1.75.0 (const: 1.75.0) | Добавляет беззнаковое смещение в байтах с оборачиванием |
wrapping_sub(self, count: usize) -> *const T | 1.26.0 (const: 1.61.0) | Вычитает беззнаковое смещение с оборачиванием |
wrapping_byte_sub(self, count: usize) -> *const T | 1.75.0 (const: 1.75.0) | Вычитает беззнаковое смещение в байтах с оборачиванием |
read(self) -> T | 1.26.0 (const: 1.71.0) | Читает значение из self, не перемещая его |
read_volatile(self) -> T | 1.26.0 | Выполняет volatile чтение значения |
read_unaligned(self) -> T | 1.26.0 (const: 1.71.0) | Читает значение из невыровненного указателя |
copy_to(self, dest: *mut T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует байты из self в dest (с перекрытием) |
copy_to_nonoverlapping(self, dest: *mut T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует байты из self в dest (без перекрытия) |
align_offset(self, align: usize) -> usize | 1.36.0 | Вычисляет смещение для выравнивания указателя |
is_aligned(self) -> bool | 1.79.0 | Проверяет, выровнен ли указатель для T |
is_aligned_to(self, align: usize) -> bool | nightly | Проверяет, выровнен ли указатель на align |
Методы для *mut T
| Метод | Версия | Описание |
|---|---|---|
is_null(self) -> bool | 1.0.0 (const: 1.84.0) | Возвращает true, если указатель нулевой |
cast<U>(self) -> *mut U | 1.38.0 (const: 1.38.0) | Приводит к указателю другого типа |
try_cast_aligned<U>(self) -> Option<*mut U> | nightly | Пытается привести к указателю другого типа с проверкой выравнивания |
with_metadata_of<U>(self, meta: *const U) -> *mut U | nightly | Использует адресное значение в новом указателе другого типа |
cast_const(self) -> *const T | 1.65.0 (const: 1.65.0) | Изменяет константность без изменения типа |
addr(self) -> usize | 1.84.0 | Получает "адресную" часть указателя |
expose_provenance(self) -> usize | 1.84.0 | Раскрывает "происхождение" указателя |
with_addr(self, addr: usize) -> *mut T | 1.84.0 | Создаёт новый указатель с заданным адресом и происхождением |
map_addr(self, f: FnOnce(usize) -> usize) -> *mut T | 1.84.0 | Создаёт новый указатель, отображая адрес на новый |
to_raw_parts(self) -> (*mut (), Metadata) | nightly | Разбирает указатель на компоненты |
as_ref<'a>(self) -> Option<&'a T> | 1.9.0 (const: 1.84.0) | Возвращает ссылку на значение или None, если null |
as_ref_unchecked<'a>(self) -> &'a T | nightly | Возвращает ссылку без проверки на null |
as_uninit_ref<'a>(self) -> Option<&'a MaybeUninit<T>> | nightly | Возвращает ссылку на MaybeUninit |
offset(self, count: isize) -> *mut T | 1.0.0 (const: 1.61.0) | Добавляет знаковое смещение |
byte_offset(self, count: isize) -> *mut T | 1.75.0 (const: 1.75.0) | Добавляет знаковое смещение в байтах |
wrapping_offset(self, count: isize) -> *mut T | 1.16.0 (const: 1.61.0) | Добавляет знаковое смещение с оборачиванием |
wrapping_byte_offset(self, count: isize) -> *mut T | 1.75.0 (const: 1.75.0) | Добавляет знаковое смещение в байтах с оборачиванием |
mask(self, mask: usize) -> *mut T | nightly | Маскирует биты указателя |
as_mut<'a>(self) -> Option<&'a mut T> | 1.9.0 (const: 1.84.0) | Возвращает изменяемую ссылку или None, если null |
as_mut_unchecked<'a>(self) -> &'a mut T | nightly | Возвращает изменяемую ссылку без проверки на null |
as_uninit_mut<'a>(self) -> Option<&'a mut MaybeUninit<T>> | nightly | Возвращает изменяемую ссылку на MaybeUninit |
guaranteed_eq(self, other: *mut T) -> Option<bool> | nightly | Проверяет гарантированное равенство указателей |
guaranteed_ne(self, other: *mut T) -> Option<bool> | nightly | Проверяет гарантированное неравенство указателей |
offset_from(self, origin: *const T) -> isize | 1.47.0 (const: 1.65.0) | Вычисляет расстояние между указателями |
byte_offset_from<U>(self, origin: *const U) -> isize | 1.75.0 (const: 1.75.0) | Вычисляет расстояние в байтах |
offset_from_unsigned(self, origin: *const T) -> usize | 1.87.0 (const: 1.87.0) | Вычисляет расстояние (self >= origin) |
byte_offset_from_unsigned<U>(self, origin: *mut U) -> usize | 1.87.0 (const: 1.87.0) | Вычисляет расстояние в байтах (self >= origin) |
add(self, count: usize) -> *mut T | 1.26.0 (const: 1.61.0) | Добавляет беззнаковое смещение |
byte_add(self, count: usize) -> *mut T | 1.75.0 (const: 1.75.0) | Добавляет беззнаковое смещение в байтах |
sub(self, count: usize) -> *mut T | 1.26.0 (const: 1.61.0) | Вычитает беззнаковое смещение |
byte_sub(self, count: usize) -> *mut T | 1.75.0 (const: 1.75.0) | Вычитает беззнаковое смещение в байтах |
wrapping_add(self, count: usize) -> *mut T | 1.26.0 (const: 1.61.0) | Добавляет беззнаковое смещение с оборачиванием |
wrapping_byte_add(self, count: usize) -> *mut T | 1.75.0 (const: 1.75.0) | Добавляет беззнаковое смещение в байтах с оборачиванием |
wrapping_sub(self, count: usize) -> *mut T | 1.26.0 (const: 1.61.0) | Вычитает беззнаковое смещение с оборачиванием |
wrapping_byte_sub(self, count: usize) -> *mut T | 1.75.0 (const: 1.75.0) | Вычитает беззнаковое смещение в байтах с оборачиванием |
read(self) -> T | 1.26.0 (const: 1.71.0) | Читает значение |
read_volatile(self) -> T | 1.26.0 | Выполняет volatile чтение |
read_unaligned(self) -> T | 1.26.0 (const: 1.71.0) | Читает из невыровненного указателя |
copy_to(self, dest: *mut T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует в dest (с перекрытием) |
copy_to_nonoverlapping(self, dest: *mut T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует в dest (без перекрытия) |
copy_from(self, src: *const T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует из src (с перекрытием) |
copy_from_nonoverlapping(self, src: *const T, count: usize) | 1.26.0 (const: 1.83.0) | Копирует из src (без перекрытия) |
drop_in_place(self) | 1.26.0 | Выполняет деструктор значения |
write(self, val: T) | 1.26.0 (const: 1.83.0) | Записывает значение без чтения старого |
write_bytes(self, val: u8, count: usize) | 1.26.0 (const: 1.83.0) | Заполняет память байтом (аналог memset) |
write_volatile(self, val: T) | 1.26.0 | Выполняет volatile запись |
write_unaligned(self, val: T) | 1.26.0 (const: 1.83.0) | Записывает в невыровненный указатель |
replace(self, src: T) -> T | 1.26.0 (const: 1.88.0) | Заменяет значение, возвращая старое |
swap(self, with: *mut T) | 1.26.0 (const: 1.85.0) | Меняет местами значения двух указателей |
align_offset(self, align: usize) -> usize | 1.36.0 | Вычисляет смещение для выравнивания |
is_aligned(self) -> bool | 1.79.0 | Проверяет выравнивание для T |
is_aligned_to(self, align: usize) -> bool | nightly | Проверяет выравнивание на align |
Методы для срезов *const [T] и *mut [T]
| Метод | Тип | Версия | Описание |
|---|---|---|---|
len(self) -> usize | оба | 1.79.0 (const) | Возвращает длину сырого среза |
is_empty(self) -> bool | оба | 1.79.0 (const) | Проверяет, пуст ли срез |
as_ptr(self) -> *const T | *const [T] | nightly | Возвращает указатель на буфер среза |
as_mut_ptr(self) -> *mut T | *mut [T] | nightly | Возвращает изменяемый указатель на буфер среза |
as_array<const N: usize>(self) -> Option<*const [T; N]> | *const [T] | 1.93.0 (const) | Преобразует в указатель на массив |
as_mut_array<const N: usize>(self) -> Option<*mut [T; N]> | *mut [T] | 1.93.0 (const) | Преобразует в изменяемый указатель на массив |
get_unchecked<I>(self, index: I) -> *const Output | *const [T] | nightly | Возвращает указатель на элемент без проверки границ |
get_unchecked_mut<I>(self, index: I) -> *mut Output | *mut [T] | nightly | Возвращает изменяемый указатель на элемент без проверки границ |
split_at_mut(self, mid: usize) -> (*mut [T], *mut [T]) | *mut [T] | nightly | Делит изменяемый срез на две части |
split_at_mut_unchecked(self, mid: usize) -> (*mut [T], *mut [T]) | *mut [T] | nightly | Делит изменяемый срез без проверки границ |
as_uninit_slice<'a>(self) -> Option<&'a [MaybeUninit<T>]> | оба | nightly | Возвращает срез MaybeUninit (разделяемый) |
as_uninit_slice_mut<'a>(self) -> Option<&'a mut [MaybeUninit<T>]> | *mut [T] | nightly | Возвращает изменяемый срез MaybeUninit |
Реализации трейтов
Общие трейты для *const T и *mut T
| Трейт | *const T | *mut T | Примечания |
|---|---|---|---|
Clone | ✓ | ✓ | Возвращает дубликат значения |
Copy | ✓ | ✓ | Тип является копируемым |
Debug | ✓ | ✓ | Форматирование для отладки |
Default | ✓ | ✓ | Возвращает null() / null_mut() |
Eq | ✓ | ✓ | Равенство указателей - отношение эквивалентности |
Hash | ✓ | ✓ | Хеширование по адресу |
Ord | ✓ | ✓ | Сравнение по адресу |
PartialEq | ✓ | ✓ | Равенство по адресу |
PartialOrd | ✓ | ✓ | Сравнение по адресу |
Pointer | ✓ | ✓ | Форматирование как указатель |
!Send | ✓ | ✓ | Не является Send |
!Sync | ✓ | ✓ | Не является Sync |
Unpin | ✓ | ✓ | Является Unpin |
UnwindSafe | ✓ | ✓ | Безопасен при раскрутке стеков |
Специальные трейты
| Трейт | Для | Описание |
|---|---|---|
AtomicPrimitive | *mut T | Атомарные операции с указателями |
From<*mut T> | AtomicPtr<T> | Преобразование *mut T в AtomicPtr<T> |
CoerceUnsized | различные | Приведение размера указателей |
DispatchFromDyn | *const T, *mut T | Динамическая диспетчеризация |
Freeze | оба | Гарантирует неизменяемость |
PinCoerceUnsized | оба | Приведение размера закреплённых указателей |
VaArgSafe | оба | Безопасен для variadic функций |
Автоматические реализации трейтов
RefUnwindSafe- реализуется, когдаT: RefUnwindSafe + ?Sized
Особенности безопасности
- Большинство операций с сырыми указателями помечены как
unsafe- компилятор не может проверить их безопасность. - Проверка выравнивания - многие операции требуют правильного выравнивания указателя.
- Проверка границ - указатели должны оставаться в пределах выделенной памяти.
- Проверка на null - многие методы имеют варианты с проверкой и без проверки на null.
- Происхождение (provenance) - современные API (
addr,with_addr) учитывают происхождение указателей для строгой модели памяти.
Примеры использования
#![allow(unused)] fn main() { // Безопасное создание и использование сырых указателей let x = 42; let ptr: *const i32 = &x; unsafe { if let Some(val) = ptr.as_ref() { assert_eq!(*val, 42); } } // Арифметика указателей let arr = [1, 2, 3, 4, 5]; let ptr = arr.as_ptr(); unsafe { let ptr2 = ptr.add(2); // Указатель на третий элемент assert_eq!(*ptr2, 3); } // Работа с изменяемыми указателями let mut arr = [1, 2, 3]; let ptr = arr.as_mut_ptr(); unsafe { *ptr.add(1) = 10; // Изменяем второй элемент } assert_eq!(arr, [1, 10, 3]); }
Важно: Все операции с сырыми указателями требуют тщательного соблюдения правил безопасности Rust, включая проверку выравнивания, границ и времени жизни.