
Size: a a a
vis
в макросах сопоставляется с описанием видимости определения, в том числе и пустым.macro_rules! accept_with_vis {
($vis:vis struct $name:ident;) => {}
}
accept_with_vis!{struct Foo;}
vis
в макросах сопоставляется с описанием видимости определения, в том числе и пустым.macro_rules! accept_with_vis {
($vis:vis struct $name:ident;) => {}
}
accept_with_vis!{struct Foo;}
macro_rules! accept_just_vis {
($vis:vis) => {}
}
accept_just_vis!{}
Drop
, нельзя деструктурировать. Однако надо отметить, что если тип поля реализует Copy
, то к этому полю можно обратиться, в том числе и пре деструктуризации, при этом значение этого поля скопируется. Проиллюстрирую только что сказанное:struct NonCopy;
struct Dropping<T>(T, NonCopy);
impl<T> Drop for Dropping<T> {
fn drop(&mut self) {
println!("dropped");
}
}
fn main() {
// ⬇️ если раскомментировать эту строку, то программа не скомпилируется с E0509
// let Dropping(_non_copy, _) = Dropping(NonCopy, NonCopy);
// А вот это — компилируется и печатает "dropped".
// Обратите внимание, паттерн `_`
*не* перемещает значение.
let Dropping(_x, _) = Dropping(0_u32, NonCopy);
}
Где это может выстрелить? При написании биндингов к сишным либам. Одна их техник обеспечения инкапсуляции в C — это определение структуры в заголовочном файле без определения её полей. Это позволяет получить доступ к полям структуры только там, где дано её определение, а внешний код может обращаться к ней лишь по указателю. И указатель — это Copy
-тип, да.extern "C" {
// extern types ещё не стабилизированы, так что
// в реальной библиотеке, скорее всего, будут использовать *mut std::ffi::c_void
type Some_C_Struct;Что произойдёт, если мы попытаемся деструктурировать
fn c_lib_release_resources(arg: *mut Some_C_Struct);
}
struct Handle(*mut Some_C_Struct);
impl Drop for Handle {
fn drop(&mut self) {
unsafe { c_lib_release_resources(self.0) }
}
}
Handle
? Скажем, нам зачем-то потребовалась реализация метода leak
:impl Handle {Что можно с этим сделать? Использовать ManuallyDrop. Этот тип предотвращает вызов деструктора (а также деструктуризацию, поскольку у него приватные поля), но при этом не предотвращает доступ к полям значения внутри (через Deref). Возникает вопрос, почему не использовать std::mem::forget? В принципе, в документации подробно расписано, но если коротко, то
fn leak(self) -> *mut Some_C_Struct {
// Здесь мы разбираем значение, но так как указатель — Copy-тип,
// имя ptr привязывается к копии значения, перемещения не происходит.
let Handle(ptr) = self; // <-- после этой точки с запятой кончается время жизни self, и вызывается деструктор
// Ура, у нас на руках невалидный указатель!
ptr
}
}
std::mem::forget
значительно сложнее корректно использовать.Vec
🎉. Первый шаг на пути к стандартным коллекциям с полностью настраиваемым аллокатором.