I am not good at English. Sorry if there are any funny expressions.
Problem
About RefCell
<MyType>
where MyType
has read-only fields.
My goal is to get those fields with reference (&
).
But from API, I can only get values wrapped with dynamic borrowing (Ref
).
I think it's strange that read-only values are wrapped with Ref
.
The way I don't like it
Of course, in the first place, I wouldn't have this trouble if I separate MyType
into two types
(One with immutable fields, and the other with RefCell
and mutable fields.)
But I think it is not cool to add type for this purpose.
My challenge with unsafe
I use unsafe
to solve this problem.
But in my experience, when I use unsafe
, I am usually confused.
I'm worried that I'm not making any sense this time, too.
So please check my code.
Here, fst_name
field is read-only, and snd_name
field is write-enabled.
Key point: fst_name
field is wrapped with my original type Rom
.
use rom::Rom;
use std::{cell::{Ref, RefCell}, ops::Deref, rc::Rc};
fn main() {
let x = Person::new("John", "Smith");
let y = Person::new("James", "Brown");
x.borrow_mut().best_friend = Some(y);
assert_eq!(x.borrow().best_friend_fst_name().unwrap(), "James");
assert_eq!(x.borrow().best_friend_snd_name().unwrap().as_str(), "Brown");
}
pub struct Person {
fst_name: Rom<String>,
snd_name: String,
best_friend: Option<Rc<RefCell<Person>>>,
}
impl Person {
pub fn new(fst_name: &str, snd_name: &str) -> Rc<RefCell<Self>> {
let fst_name = Rom::new(fst_name.to_string());
let snd_name = snd_name.to_string();
let ret = Self { fst_name, snd_name, best_friend: None };
Rc::new(RefCell::new(ret))
}
pub fn best_friend_fst_name(&self) -> Option<&str> {
let bf = self.best_friend.as_ref().map(|x| x.deref())?;
Some(Rom::map_ref_cell(bf, |x| &x.fst_name))
}
pub fn best_friend_snd_name(&self) -> Option<Ref<String>> {
let bf = self.best_friend.as_ref().map(|x| x.deref())?;
Some(Ref::map(bf.borrow(), |x| &x.snd_name))
}
}
mod rom {
use std::{cell::RefCell, ops::Deref};
pub struct Rom<T: ?Sized>(T);
impl<T> Rom<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T: ?Sized> Rom<T> {
pub fn map_ref_cell<C>(cell: &RefCell<C>, f: impl Fn(&C) -> &Self) -> &T {
&f(unsafe { &*(cell.as_ptr() as *const _) }).0
}
}
impl<T: ?Sized> Deref for Rom<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
}