Closed npuichigo closed 1 year ago
Disclaimer: I'm not the author of that code.
I guess by "down casting directly," you mean something like fn($in_ty) -> Option<$out_ty>
, but the downcasting methods provided by the standard library are only implemented for the trait object type dyn Any
, which (in current stable Rust) you can only use through a reference like Box<dyn Any>
or &mut Any
. Creating a Box<dyn Any>
incurs an allocation cost so fn downcast_mut<T>(&mut dyn Any) -> Option<&mut T>
is the preferable method here.
Naively, one might attempt to get a &mut $out_ty
with that method, but you cannot simply take an owned $out_ty
value out of the borrowed reference. If you could provide a placeholder value like <$out_ty>::default()
, you could mem::swap
the referent with the placeholder to get an owned value, but $out_ty
is an arbitrary type which won't even let you get such a placeholder.
Here is where the Option
trick plays the role. downcast_mut::<Option<$out_ty>>()
gives you an &mut Option<$out_ty>
, on which you can just call take()
to get the owned $out_ty
value, which is equivalent to providing the None
value as a placeholder.
One could still think of an alternative way that involves transmute
, but that would be unsafe and I'm not sure doing that would even be sound.
I guess the goal of the macro is to replace $val
with a downcast value to make $body
compile (which contains the name $val
). Without the Option
trick, other alternatives would be something like
let $val = *(Box::new($val) as Box<dyn std::any::Any>).downcast().unwrap();
$body
with extra allocations
if let Some(ref_val) = (&$val as &dyn std::any::Any)
.downcast_ref::<$out_ty>()
{
let $val = ref_val.clone();
$body
}
with extra copies
Thanks for your guys' detailed answers!
https://github.com/hyperium/http/blob/34a9d6bdab027948d6dea3b36d994f9cbaf96f75/src/convert.rs#L4-L6
Can anyone help to explain why we need to wrap the value into Option before downcast instead of down casting directly?