If I retain a NonNull<T>
to some value deep within the bowels of the data pointed to by an Arc
, is it guaranteed that that pointer will always be valid for as long as the Arc
is alive? That is, if I keep a reference to the Arc
in the same struct, will it always be safe to dereference the pointer?
struct Element(i64); // pretend this is actual useful data
struct Collection {
// in the actual code, for reasons I don't think are relevant, these cannot be merged
data1: Box<[Element]>,
data2: Box<[Element]>,
}
impl Collection {
pub fn perform_some_search(&self, some_args: FooType) -> impl Iterator<Item=&Element> {
// returns a combination of elements from data1 and data2
}
}
// for reasons I won't go into, I need this struct to be 'static
struct SearchResults {
collection: Arc<Collection>,
items: Vec<NonNull<Element>>,
}
impl SearchResults {
pub fn memorize_search_results(collection: Arc<Collection>, some_args: FooType) -> Self {
let items = collection.perform_some_search(some_args).map(NonNull::from).collect::<Vec<_>>();
Self {collection, items}
}
// note that the lifetime returned here is the lifetime of the SearchResults which is (at least
// in theory) strictly shorter than the lifetime of the Collection, thanks to the Arc.
// my question is, is this sufficient to guarantee soundness?
pub fn iterate_search_results(&self) -> impl Iterator<Item=&Element> {
self.items.iter().map(|ptr| unsafe {ptr.as_ref()})
}
}
Is the above code sound? Since, modulo any UnsafeCell
inside, the contents of an Arc
cannot be mutated, is it also true that they cannot move, thus guaranteeing pointer validity for as long as the Arc
is alive? Can I safely rely on that? Would wrapping the Arc in a Pin, i.e. Pin<Arc<Collection>>
, do anything at all?
Can I get away with this without dreaming up some harebrained scheme like
enum WhichData {Data1, Data2}
struct Index(WhichData, usize);
impl Collection {
pub fn perform_some_search(&self, some_args: FooType) -> impl Iterator<Item=Index> {
// returns a combination of elements from data1 and data2
}
pub fn get(&self, index: Index) -> &Element {
match index.0 {
WhichData::Data1 => &self.data1[index.1],
WhichData::Data2 => &self.data2[index.1],
}
}
}
and doing a buttload of work to rework all of my existing code to take the Index
struct instead of a reference (or pointer)?