When writing callbacks for generic interfaces, it can be useful for them to define their own local data which they are responsible for creating and accessing.
In C I would just use a void pointer, C-like example:
struct SomeTool {
int type;
void *custom_data;
};
void invoke(SomeTool *tool) {
StructOnlyForThisTool *data = malloc(sizeof(*data));
/* ... fill in the data ... */
tool.custom_data = custom_data;
}
void execute(SomeTool *tool) {
StructOnlyForThisTool *data = tool.custom_data;
if (data.foo_bar) { /* do something */ }
}
When writing something similar in Rust, replacing void *
with Option<Box<Any>>
, however I'm finding that accessing the data is unreasonably verbose, eg:
struct SomeTool {
type: i32,
custom_data: Option<Box<Any>>,
};
fn invoke(tool: &mut SomeTool) {
let data = StructOnlyForThisTool { /* my custom data */ }
/* ... fill in the data ... */
tool.custom_data = Some(Box::new(custom_data));
}
fn execute(tool: &mut SomeTool) {
let data = tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap();
if data.foo_bar { /* do something */ }
}
There is one line here which I'd like to be able to write in a more compact way:
tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap()
tool.custom_data.as_ref().unwrap().downcast_mut::<StructOnlyForThisTool>().unwrap()
While each method makes sense on its own, in practice it's not something I'd want to write throughout a code-base, and not something I'm going to want to type out often or remember easily.
By convention, the uses of unwrap here aren't dangerous because:
- While only some tools define custom data, the ones that do always define it.
- When the data is set, by convention the tool only ever sets its own data. So there is no chance of having the wrong data.
- Any time these conventions aren't followed, its a bug and should panic.
Given these conventions, and assuming accessing custom-data from a tool is something that's done often - what would be a good way to simplify this expression?
Some possible options:
- Remove the
Option
, just useBox<Any>
withBox::new(())
representingNone
so access can be simplified a little. - Use a macro or function to hide verbosity - passing in the
Option<Box<Any>>
: will work of course, but prefer not - would use as a last resort. - Add a trait to
Option<Box<Any>>
which exposes a method such astool.custom_data.unwrap_box::<StructOnlyForThisTool>()
with matchingunwrap_box_mut
.
Update 1): since asking this question a point I didn't include seems relevant.
There may be multiple callback functions like execute
which must all be able to access the custom_data
. At the time I didn't think this was important to point out.
Update 2): Wrapping this in a function which takes tool
isn't practical, since the borrow checker then prevents further access to members of tool
until the cast variable goes out of scope, I found the only reliable way to do this was to write a macro.