Как клонировать Rc из метода, который принимает &self

У меня есть структура Region, которая содержит [Option<Rc<RefCell<Region>>>; 2] как subregions и Option<Weak<RefCell<Region>>> как container. Я пишу метод для вставки нового Region между self и self.container, который требует от меня клонировать Rc в self, чтобы включить его во вновь созданный Region как один из его subregions. Когда self.container есть Some, это возможно, потому что я могу подойти к контейнеру и затем клонировать его subregion, который соответствует self, но когда self имеет None в качестве контейнера, я не знаю, как получить клон Rcself из &mut self который передается в метод.

Вот код, чтобы было понятнее.

struct Region {
    subregions: [Option<Rc<RefCell<Region>>>; 2],
    container: Option<Weak<RefCell<Region>>>,
}

impl Region {
    fn clone_from_container(&self) -> Option<Rc<RefCell<Region>>> {
        Some(Rc::clone(&self.container
                       .as_ref()?
                       .upgrade()
                       .unwrap()
                       .borrow()
                       .subregions[self.from()]
                       .as_ref()
                       .unwrap()))
    }

    pub fn insert_above(&mut self) -> Rc<RefCell<Region>> {
        let new = Rc::new(RefCell::new(Region {
            subregion: [self.clone_from_container(),None],
            container: self.container.clone(),
        }));
        self.replace(new.clone());
        self.container = Some(Rc::downgrade(&new.clone()));
        new
    }
}

В моем реальном коде есть еще несколько функций, но это всего лишь псевдонимы для длинных цепочек методов, позволяющих распаковать Rc и RefCell и позволить мне фактически получить данные. Я постарался ограничиться только важными деталями.

Исключенные мной методы Region::replace и Region::from довольно просты, и их реализация не имеет значения. replace подходит к container и заменяет ссылку на self новым регионом (очень похоже на clone_from_container, но изменяет Region), а from просто находит индекс Region внутри своих containersubregions (0 или 1)

При установке subregion из new внутри я хотел бы иметь возможность сказать что-то вроде self.clone_from_container().or(Rc::clone(self)), но очевидно, что это невозможно, учитывая тип, в качестве которого передается self.

Я полагаю, что, возможно, просто необходимо, чтобы это была статическая фабричная функция, а не метод экземпляра, чтобы мы могли передавать self с типом Rc<RefCell<Region>>, но я также подумал, что можно использовать Rc::new_cyclic; однако эта функция несколько сбивает с толку, и я не совсем понимаю, как ее использовать.

self может быть Rc<Self> именно RefCell делает это невозможным.

kmdreko 18.03.2024 21:58

Другой вариант — создать служебный признак, реализованный для Rc<RefCell<Region>>, тогда вы сможете использовать вызовы методов как обычно, если импортируете признак.

PitaJ 18.03.2024 22:22

Я рассмотрю характеристику полезности, о которой вы упомянули; если это не сделает код более нечитаемым или что-то в этом роде, я могу это сделать, но в противном случае мне просто придется изменить метод на функцию, которая принимает Rc<RefCell<Region>> в качестве параметра :)

user21749640 18.03.2024 23:13
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
3
93
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Для упрощения я буду говорить Rc<Region> вместо Rc<RefCell<Region>>.

Если я правильно понял, это ваша ситуация:

let region = Region::new()
let rc = Rc::new(region)

И у вас есть такой метод, как:

impl Region {
    fn my_function(&mut self) {
        // Here you want to somehow get a reference to `rc` (the Rc that contains this region)
    }
}

Это невозможно, потому что Region не знает, что находится внутри Rc. Вы должны каким-то образом сообщить ему, что он находится внутри Rc.

Одно из возможных решений — не использовать self. Например:

impl Region {
    fn my_function(value: Rc<Self>) {
        // Now you have access to `rc`
    }
}

Другой вариант, как предложил PitaJ, — создать черту для Rc<Region>, в этом случае self будет Rc<Region>, чтобы у вас был доступ к Rc.

Вам не нужно создавать черту для Rc<Region>, параметр self может быть Rc<Region>: игровая площадка (но это не работает с RefCell)

Jmb 19.03.2024 09:40

Есть такая причудливая штука Rc::new_cycle, которая позволяет вам сохранять указатель Weak в объект, который вы создаете.

Если повторно использовать упрощение @CalcOPH из другого ответа, это будет что-то вроде:

struct Region {
    this: Weak<Self>,
    // other members
}
impl Region {
    fn new() -> Rc<Self> {
        Rc::new_cyclic(|this| {
            Region {
                this: this.clone(),
                // other members
            }
        })
    } 
    fn my_function(&mut self) -> Rc<Self> {
        // unwrap() cannot fail, unless called from `drop()`
        // because this object exists.
        self.this.upgrade().unwrap().clone()
    }
}

Естественно, это потребует от вас написания нового немного по-другому, но, возможно, оно того стоит.

Другие вопросы по теме