Обновить

Комментарии 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 для полей. Но м.б. вы что-то другое имели в виду.

Но в целом статья хорошая.

LLMка еще может путаться/галюцинировать. Я вон в другой статье прочел что если вернуть значение из функции как ссылку ( со *) то компилятор разместит значение в куче.

Ждал упоминание 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 всё ещё владеет своим старым значением, и его деструктор вызовется автоматически при выходе из функции.

Про это уже сказали выше - здесь будет утечка старого значения.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
beget.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия