Комментарии 9
Хорошая статься, но есть пару вводящих в заблуждение моментов:
Правда, утечка памяти осталась бы на нашей совести.
Какая утечка памяти? Мы же уже выяснили, что ptr::drop_in_place освободил ее, а mem::forget сказал компилятору, что там освобождено и предотвратил double free.
В данном случае это даже супер, потому что переменная
xвсё ещё владеет своим старым значением, и его деструктор вызовется автоматически при выходе из функции.
Ничего не супер, в x теперь же "New", а для "Old" drop так и не вызвался (new_value перемещен внутрь ptr::write, который его затем закидывает в x, поэтому для него drop не вызывается)
# struct Person { name: String, age: u8 }...
Как же быть, если всё‑таки очень нужно вынуть поле? Вариант: использовать функции вродеstd::mem::replaceилиstd::mem::take, которые позволяют заменить значение в поле на какое‑то пустое, а старое значение вернуть.
Тут лучше другой вариант: подумать ещё пять раз, а так ли нужно изменять экземпляр Person, и не лучше ли настучать по рукам тому, кто объявляет структуру изменяемой:
let mut p = Person {...}После трюка let name = mem::replace(&mut p.name, String::new()); уже нельзя использовать саму структуру, но узнаете это вы только после того, как всё же сделаете это через триста строк и потратите кучу времени на вылавливание бага, который проявляется только в рантайме.
а какой баг появится, понимаю, что тут про изменение полей, я присмотрелся, тоже создаю большую структуру мира, и использую мут везде где можно, мир рисуется 30 мегабайт система показывает, интересно, какие в mut подводные камни, у меня уже многопоточно строится мир, через каналы, и тоже мутабильность при получении чанков, а чанки тоже структуры-модули ), вообще Раст оказался интересный, поля я правда не меняю нигде и дроп нигде не трогаю
я заметил, ситуацию с хэш мапой можно елегантно решить через каналы ) просто получать данные и вставлять без удалений, как я понимаю
старый кусочек без потоков, я сразу заметил тут какая-то магия )
if let Some(mut ch) = self.chunks.remove(&cpos) {
// ссылка на World тут я рисую полностью полый мир
ch.build_after_generate_chunk(&self);
ch.build_chunkw(); // текстура, буфер, координаты
//chunk.rebuild_vbufs();
self.chunks.insert(cpos, ch);
}похожая ситуая, я её обошел многопоточкой неблокирующей
я не експерт, просто где-то видел и решил показать, как наверно обходят дроп, в Расте мы же можем говорить компилятору чо делать)
Скрытый текст
pub struct Person { x: String, y: u8 }
impl Person {
pub fn remove(P: &mut Person) -> Option<Person>{
Some(Person{x:"".to_string(),y:0})
}
}
fn main() {
let mut p = Person { x: "q".into(), y: 0 };
println!("{}",p.x);
if let Some(mut qqq) = Person::remove(&mut p){
println!("{}",qqq.x);
};
}По дефолту Rust генерирует код деструктора для структуры рекурсивно. Сначала вызывается drop для полей, потом выполняется ваш код в Drop.
Вроде как наоборот - сначала вызывается наш Drop, потом drop для полей. Но м.б. вы что-то другое имели в виду.
Но в целом статья хорошая.
Ждал упоминание std::mem::ManuallyDrop, но не увидел, хотя штука тоже может больно отстрелить.
https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html
Или убрать создание экземпляра Foo и работу с ним во вложенный блок. Тогда к моменту println время жизни foo уже завершится - и, соответственно, data уже не будет borrowed.
Скрытый текст
struct Foo<'a, T>(&'a mut T);
impl<'a, T> Drop for Foo<'a, T> {
fn drop(&mut self) {
println!("Drop для Foo отработал");
}
}
fn main() {
let mut data = String::from("example");
{
let foo = Foo(&mut data);
// ... работаем с foo ...
}
println!("data = {}", data);
}Функция
ptr::drop_in_placeвыполняет деструктор по указанному адресу, по сути освобождая память, на которую указываетnum_ptr. После её вызова ресурс уже освобождён, но переменнаяnumпо‑прежнему существует и при выходе из области видимости снова попробует освободить тот же самый адрес. Это классическая ситуация double free.
Вовсе не так: у вас же num_ptr: *mut i32, то есть никакого деструктора для числа нет, тем более освобождающего память.
Надо было оперировать указателем на Box, чтобы получить double free.
В данном случае это даже супер, потому что переменная
xвсё ещё владеет своим старым значением, и его деструктор вызовется автоматически при выходе из функции.
Про это уже сказали выше - здесь будет утечка старого значения.
Осторожно, Drop: как невинный деструктор рушит код