У меня есть структура 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
в качестве контейнера, я не знаю, как получить клон Rc
self
из &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
внутри своих container
subregions
(0 или 1)
При установке subregion
из new
внутри я хотел бы иметь возможность сказать что-то вроде self.clone_from_container().or(Rc::clone(self))
, но очевидно, что это невозможно, учитывая тип, в качестве которого передается self
.
Я полагаю, что, возможно, просто необходимо, чтобы это была статическая фабричная функция, а не метод экземпляра, чтобы мы могли передавать self
с типом Rc<RefCell<Region>>
, но я также подумал, что можно использовать Rc::new_cyclic
; однако эта функция несколько сбивает с толку, и я не совсем понимаю, как ее использовать.
Другой вариант — создать служебный признак, реализованный для Rc<RefCell<Region>>
, тогда вы сможете использовать вызовы методов как обычно, если импортируете признак.
Я рассмотрю характеристику полезности, о которой вы упомянули; если это не сделает код более нечитаемым или что-то в этом роде, я могу это сделать, но в противном случае мне просто придется изменить метод на функцию, которая принимает Rc<RefCell<Region>>
в качестве параметра :)
Для упрощения я буду говорить 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
)
Есть такая причудливая штука 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()
}
}
Естественно, это потребует от вас написания нового немного по-другому, но, возможно, оно того стоит.
self может быть Rc<Self> именно
RefCell
делает это невозможным.