So I know it is UB to cast a Immutable Reference to a Mutable reference. However, is it still considered UB if the Immutable Reference was derived from a Mutable Reference, as technically the memory should already be treated as mutable? Or would this still be UB, as the fact the Immutable Reference was once mutable completely irrelevant, and it will still treat the reference as Immutable, given that casting from mutable -> immutable consumes the original mutable reference?
More concretely, say I have a tree structure like this:
pub struct Schema {
pub name: String,
pub fields: Vec<SchemaNode>,
pub types: BTreeMap<String, ComplexType>,
}
pub struct SchemaNode {
pub name: String,
pub dtype: DType,
}
pub enum DType {
Base(),
Complex {
name: String,
},
}
pub struct ComplexType {
pub children: Vec<SchemaNode>,
}
impl Schema {
pub fn query(&self, query: &str) -> Option<&SchemaNode> {
let mut fragments = query.split(".");
let name = fragments.next()?;
let mut node = self.fields.iter().filter(|n| n.name == name).next()?;
// continue down if required
for name in fragments {
match &node.dtype {
DType::Base => return None,
DType::Complex { name: type_name } => {
node = self
.types
.get(type_name)?
.children
.iter()
.filter(|c| c.name == name)
.next()?;
}
};
}
Some(node)
}
}
If I wanted to add a query_mut method, would it be OK to do it like this:
impl Schema {
pub fn query_mut<'a>(&'a mut self, query: &str) -> Option<&'a mut SchemaNode> {
let node = self.query(query)?;
unsafe {
let const_ptr = node as *const SchemaNode;
let mut_ptr = const_ptr as *mut SchemaNode;
mut_ptr.as_mut()
}
}
}
In this case, I understand that one could write query_mut, then derive query from query_mut, but as it's currently structured, you would run into mutability issues, as Schema.types would get borrowed multiple times inside the loop.
Yes, this is invalid.
To quote the nomicon:
- Transmuting an
&
to&mut
is Undefined Behavior. While certain usages may appear safe, note that the Rust optimizer is free to assume that a shared reference won't change through its lifetime and thus such transmutation will run afoul of those assumptions. So:
- Transmuting an
&
to&mut
is always Undefined Behavior.- No you can't do it.
- No you're not special.
Writing a separate query_mut()
is the correct way to do that.