I met a situation where I don't know how to avoid using unsafe
and not write almost the same code twice.
Here we have an enum named Tree
:
use std::collections::HashMap;
enum Tree {
KV(HashMap<String, Tree>),
Value(String),
}
Now we want to define a function which could find Tree
by path splitted with ".
".
impl Tree {
fn find_element(&self, path: &str) -> Option<&Tree> { todo!() }
fn find_element_mut(&mut self, path: &str) -> Option<&mut Tree> { todo!() }
}
For example:
let ele: &Tree = tree.find_element("find.element.by.path").unwrap();
I wrote find_element
as follow:
impl Tree {
fn find_element(&self, path: &str) -> Option<&Tree> {
let mut prv = self;
assert!(path.is_ascii());
for name in path.split(".") {
match prv {
Tree::KV(map) => match map.get(name) {
Some(tree) => prv = tree,
None => return None,
},
Tree::Value(s) => return None,
}
}
Some(prv)
}
}
Now here comes the problem: How to write find_element_mut
?
I have two solutions. The first one is to copy the code and edit it, so we got this:
impl Tree {
fn find_element(&self, path: &str) -> Option<&Tree> {
let mut prv = self;
assert!(path.is_ascii());
for name in path.split(".") {
match prv {
Tree::KV(map) => match map.get(name) {
Some(tree) => prv = tree,
None => return None,
},
Tree::Value(s) => return None,
}
}
Some(prv)
}
fn find_element_mut(&mut self, path: &str) -> Option<&mut Tree> {
let mut prv = self;
assert!(path.is_ascii());
for name in path.split(".") {
match prv {
Tree::KV(map) => match map.get_mut(name) {
Some(tree) => prv = tree,
None => return None,
},
Tree::Value(s) => return None,
}
}
Some(prv)
}
}
The second solution is to use unsafe
to avoid writing duplicate code:
impl Tree {
unsafe fn _find_element(slf: *const Tree, path: &str) -> Option<*const Tree> {
let mut prv = &*slf;
assert!(path.is_ascii());
for name in path.split(".") {
match prv {
Tree::KV(map) => match map.get(name) {
Some(tree) => prv = tree,
None => return None,
},
Tree::Value(s) => return None,
}
}
Some(prv)
}
fn find_element(&self, path: &str) -> Option<&Tree> {
unsafe { Self::_find_element(self, path).map(|e| &*e) }
}
fn find_element_mut(&mut self, path: &str) -> Option<&mut Tree> {
unsafe { Self::_find_element(self, path).map(|e| &mut *(e as *mut Tree)) }
}
}
The above two solutions both seem to be not satisfying. Does anybody have any good ideas?