К сожалению, оказывается, что API Rc
не имеет необходимого способа, чтобы получить право собственности на обернутый тип, если он равен !Sized
.
Единственный способ, который может вернуть внутренний элемент в Rc
является Rc::try_unwrap
, однако он возвращает Result<T, Rc<T>>
, который требует, чтобы T
быть Sized
.
Для того, чтобы сделать то, что вы хотите, вы должны были бы иметь метод с подписью: Rc<T> -> Result<Box<T>, Rc<T>>
, что позволило бы T
быть !Sized
, и оттуда можно извлечь Box<Any>
и выполнить downcast
вызов.
Однако этот способ невозможно из-за того, как реализован Rc
.Вот урезанная версия Rc
:
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
pub struct Rc<T: ?Sized> {
ptr: *mut RcBox<T>,
_marker: PhantomData<T>,
}
Таким образом, только Box
вы можете выйти из Rc<T>
является Box<RcBox<T>>
.
Обратите внимание, что конструкция сильно ограничена здесь:
- мандаты одного распределения, что все 3 элемента будут в едином
struct
T: ?Sized
мандатов, которые T
быть последним полем
так в целом нет возможности для улучшения.
Однако в вашем конкретном случае, безусловно, возможно улучшить общую ситуацию. Конечно, это требует кода unsafe
. И хотя он неплохо работает с Rc
, реализация его с Arc
будет сложна потенциальными раслетами данных.
Ох ... и код предоставляется как есть, без гарантии подразумевается;)
use std::any::Any;
use std::{cell, mem, ptr};
use std::rc::Rc;
struct RcBox<T: ?Sized> {
strong: cell::Cell<usize>,
_weak: cell::Cell<usize>,
value: T,
}
fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
// Will be responsible for freeing the memory if there is no other weak
// pointer by the end of this function.
let _guard = Rc::downgrade(&rc);
unsafe {
let killer: &RcBox<Any> = {
let killer: *const RcBox<Any> = mem::transmute(rc);
&*killer
};
if killer.strong.get() != 1 { return None; }
// Do not forget to decrement the count if we do take ownership,
// as otherwise memory will not get released.
let result = killer.value.downcast_ref().map(|r| {
killer.strong.set(0);
ptr::read(r as *const T)
});
// Do not forget to destroy the content of the box if we did not
// take ownership
if result.is_none() {
let _: Rc<Any> = mem::transmute(killer as *const RcBox<Any>);
}
result
}
}
fn main() {
let x: Rc<Any> = Rc::new(1);
println!("{:?}", concretify::<i32>(x));
}
Существует диссонанс здесь: 'Вариант' * собственный * 'T', в то время как' 'Rc является только одним из совладельцев * объекта. Я могу видеть два способа обработки этого: возврат 'None', если есть несколько владельцев или преобразование функции для преобразования из' & Rc 'в' Опция <&T> '. Является первым, что вы хотите? –
Вот что ['try_unwrap'] (https://doc.rust-lang.org/std/rc/struct.Rc.html#method.try_unwrap) для - он гарантирует, что данный' Rc' является только владелец. «concretify'» должен возвращать 'None', если есть несколько владельцев или если' Any' не 'T'. –
Просто проверка :) В этом случае, вы в порядке с «Нет» или вы скорее вернете «Результат», который указывает в поле «Err», является ли проблема несоответствующим типом или несколькими владельцами? –