Есть ли способ добавить элементы в контейнер, неизменно заимствуя более ранние элементы?

Я создаю графический интерфейс и хочу хранить все используемые текстуры в одном месте, но мне нужно добавить новые текстуры, в то время как старые текстуры уже безвозвратно заимствованы.

let (cat, mouse, dog) = (42, 360, 420); // example values

let mut container = vec![cat, mouse]; // new container

let foo = &container[0]; // now container is immutably borrowed

container.push(dog); // error: mutable borrow

Существует ли какая-либо существующая структура, которая позволяет что-то подобное, или я могу реализовать что-то подобное, используя необработанные указатели?


person lncr    schedule 25.07.2017    source источник


Ответы (1)


Абсолютно проще всего ввести совместное владение:

use std::rc::Rc;

fn main() {
    let (cat, mouse, dog) = (42, 360, 420);
    let mut container = vec![Rc::new(cat), Rc::new(mouse)];
    let foo = container[0].clone();
    container.push(Rc::new(dog));
}

Теперь container и foo совместно владеют cat.

Есть ли какая-либо существующая структура, которая позволяет что-то подобное,

Да, но всегда есть компромиссы. Выше мы использовали Rc для совместного владения, что включает в себя счетчик ссылок.

Другим потенциальным решением является использование арены:

extern crate typed_arena;

use typed_arena::Arena;

fn main() {
    let container = Arena::new();
    let cat = container.alloc(42);
    let mouse = container.alloc(360);
    let dog = container.alloc(420);
}

Это не индексируется, вы не можете снова стать владельцем значения и не можете удалить значение.

Возможность удалять объекты из коллекции всегда делает недействительные ссылки опасными.

могу ли я реализовать что-то подобное, используя необработанные указатели

Почти наверняка. Будете ли вы получить это право всегда сложный момент.

но я должен добавить новые текстуры, в то время как старые текстуры уже безвозвратно заимствованы

Во многих случаях вам необходимо делать что-либо подобное. Например, вы можете разделить свою логику на этапы. У вас есть два контейнера; один, на который люди берут ссылки, и другой, чтобы собирать новые значения. В конце фазы вы объединяете две коллекции в одну. Конечно, вы должны убедиться, что никакие ссылки не используются после окончания фазы.

person Shepmaster    schedule 25.07.2017