I don't think there is any possibility of obtaining a Ref<T>
given only a &T
. You can only ever obtain Ref<T>
by calling RefCell::<T>::borrow()
, since the Ref
type has no public constructor, and Ref::map
requires the mapped function to return a reference, not a value possibly wrapping a reference.
I can see two(-ish) ways to rewrite this:
- "Shift the blame" (or burden) to the caller: just return a
Ref
into the inner Foo
and let the caller use this ref for calling get_i32
on the inner Foo
.
- Write your own
map
function that obtains the Ref
internally and hides it, then calls the supplied closure only if the inner getter returned Some
. A slight variant on this theme would be calling the closure all cases, but with an Option
as its argument.
Here's a playground:
use std::cell::{ RefCell, Ref };
pub enum Foo {
I32(Vec<i32>),
F32(Vec<f32>),
}
impl Foo {
fn get_i32(&self) -> Option<&[i32]> {
match *self {
Foo::I32(ref vec) => Some(vec),
Foo::F32(_) => None,
}
}
fn get_f32(&self) -> Option<&[f32]> {
match *self {
Foo::I32(_) => None,
Foo::F32(ref vec) => Some(vec),
}
}
}
pub struct Bar {
data: RefCell<Foo>
}
impl Bar {
// First method: shift the burden to the caller
fn as_ref(&self) -> Ref<Foo> {
self.data.borrow()
}
// Second method: get rid of the ref altogether.
// You wouldn't ref in public, would you?
fn map<T, R, G, F>(&self, getter: G, func: F) -> Option<T>
where R: ?Sized,
G: FnOnce(&Foo) -> Option<&R>,
F: FnOnce(&R) -> T,
{
getter(&*self.data.borrow()).map(func)
}
// Or, if you always want the closure to be called:
fn map_opt<R, G, F>(&self, getter: G, func: F)
where R: ?Sized,
G: FnOnce(&Foo) -> Option<&R>,
F: FnOnce(Option<&R>),
{
func(getter(&*self.data.borrow()));
}
}
fn main() {
let bar = Bar {
data: RefCell::new(Foo::I32(vec![1, 2, 3]))
};
// First method
let inner = bar.as_ref();
let maybe_i32 = inner.get_i32();
let maybe_f32 = inner.get_f32();
println!("shift the blame i32: {:?}", maybe_i32);
println!("shift the blame f32: {:?}", maybe_f32);
// Second method
bar.map(Foo::get_i32, |x| println!("to call or not to call i32: {:?}", x));
bar.map(Foo::get_f32, |x| println!("to call or not to call f32: {:?}", x)); // not printed at all
// Or even
let cloned_i32 = bar.map(Foo::get_i32, ToOwned::to_owned);
let cloned_f32 = bar.map(Foo::get_f32, ToOwned::to_owned);
println!("to owned and return i32: {:?}", cloned_i32);
println!("to owned and return f32: {:?}", cloned_f32);
// 2.5th method
bar.map_opt(Foo::get_i32, |x| println!("always call but maybe with nothing i32: {:?}", x));
bar.map_opt(Foo::get_f32, |x| println!("always call but maybe with nothing f32: {:?}", x));
}