macro_rules! unsafe_delegate_structural_with {
(
$( #[doc=$doc:expr] )*
impl $impl_params:tt $self:ty
where $where_clause:tt
self_ident=$this:ident;
$( specialization_params($($raw_mut_impl:tt)*); )?
delegating_to_type=$delegating_to_type:ty;
$($rest:tt)*
) => { ... };
}Expand description
This macro allows delegating the implementation of the accessor traits.
This macro delegates the implementation of those traits for all fields, it doesn’t provide a way to do so for only a list of fields.
§Safety
In order to use this macro soundly,
you must ensure that side effects (including mutation) do not happen while
getting the delegated-to variable,
and that the delegated-to variable has a consistent value when
methods from structural traits are called in a sequence
(with no method calls from non-structural-traits in between).
You must ensure that the variable that you delegate Get*Field to is the same as the one
you delegate Get*FieldMut/Into*Field to,
as well as ensuring that there are no other impls of the Get*FieldMut/Into*Field traits
accessing the same delegated-to variable.
§Drop behavior
When converting a type into multiple fields by value
(using [StructuralExt::into_fields] or [StrucWrapper::vals]),
the regular destructor (Drop::drop) won’t run,
instead the steps in the section below will happen.
If your type has code that must run when it’s dropped,
you can pass DropFields= { ..... pre_move = foo_function; } to this macro
to run that code before the type is converted into multiple fields by value
(using [StructuralExt::into_fields] or [StrucWrapper::vals]),
§Drop order
The order of operations when invoking [StructuralExt::into_fields] or [StrucWrapper::vals]
is this by default:
-
Call
DropFields::pre_move. -
Move out the fields.
-
Drop the fields passed to
dropped_fields[], in the order that they were passed. -
Drop the delegated-to variable with
DropFields::drop_fields. -
Return the moved out fields
§Example with all syntax
use structural::unsafe_delegate_structural_with;
struct Foo<T>{
a: (),
b: (),
c: (),
d: Bar,
value:T
}
struct Bar{
e: (),
}
unsafe_delegate_structural_with!{
impl[T,] Foo<T>
// This where clause is required syntax
where[
T:Trait,
]
// This is the identifier used for `self` in the blocks below.
self_ident=this;
// An optional macro argument that tells it how to specialize pointer methods.
//
// `specialization_params(Sized);` is the default when this the parameter is not passed,
// it means that no specialization is used,always requiring `Self:Sized`.
//
// This is the default value for the parameter:
// `specialization_params(Sized);`
// This means that the type is `?Sized` and not specialization is used,
// this may be slower in debug builds because this always uses a
// function pointer call in raw-pointer-taking methods.
//
// `specialization_params(?Sized);`
// This means that the type is `?Sized` by default.
// The `cfg(anything)` argument enables specialization conditionally,
//
// When specialization is disabled theres only a default impl for `Self:?Sized`
// which may be slower in debug builds,
// because this uses a function pointer call in raw-pointer methods.
//
// When specialization is enabled,the impl is specializes on `Self:Sized`
// to remove the overhead of raw-pointer methods.
//
// `specialization_params(cfg(anything));`
// This is the type of the variable we delegate to,
// this is required because Rust doesn't have a `typeof`/`decltype` construct.
delegating_to_type=T;
// This block of code is used to get the reference to the delegated-to variable
// in GetField.
GetField {
&this.value
}
// This block of code is used to get a mutable reference to the delegated-to variable
// in GetFieldMut
//
// This block must always evaluate to a mutable reference for the same variable,
// and it must not be the same variable as other implementations of the GetFieldMut trait.
//
GetFieldMut
where [
// This is an optional where clause
// The last where predicate must have a trailing comma.
T:Trait,
]{
&mut this.value
}
// This gets a raw mutable pointer to the variable this delegates to.
as_delegating_raw{
&mut (*this).value as *mut T
}
// This block of code is used to get the delegated-to variable in IntoField::into_field.
IntoField
where [
// This is an optional where clause
// The last where predicate must have a trailing comma.
T:Trait,
]{
this.value
}
// This block of code is used to get the delegated-to variable in
// IntoField::move_out_field_.
move_out_field{
&mut this.value
}
// `DropFields = .... ` determines what happens in the DropFields implementation
// for the type, which isn't as trivial as delegating to the delegated_to type.
//
DropFields = {
// Which fields are dropped in `DropFields::drop_fields`,
// in the order that they're dropped.
//
// If you're dropping a nested field,it must be wrapped in parentheses(ie:`(a.b.c)`).
dropped_fields[ a,b,c,(d.e) ]
// An optional argument for code to run right before fields are moved.
//
// `some_function` is called in the implementation of
// `DropFields::pre_move` generated for this type.
//
// The passed in function must have this signature: `fn(&mut Self)`
// in this example it would be `fn<T: Trait>(&mut Foo<T>)`
//
// `pre_move = some_function;`
// An optional argument which determines whether there's pre/post field-drop logic
// in `DropFields::drop_fields`.
//
// With `pre_post_drop_fields=true;` this inserts
// a `PrePostDropFields::pre_drop(self)` call before the fields are dropped,
// and a `PrePostDropFields::post_drop(self)` call after the fields are dropped,
//
// To use this the type must implement the `PrePostDropFields` trait
// to do something before and after the listed fields (and the delegated_to variable)
// are dropped.
//
// This is the default value:
// `pre_post_drop_fields=false;`
// Whether to drop the fields in the delegated_to variable,with `DropFields::drop_fields`.
//
// With `drop_delegated_drop_variable=true;`,
// this calls `DropFields::drop_fields` on the delegated to variable.
//
// With `drop_delegated_drop_variable=false;`,
// it does not drop the delegated to variable in an way.
//
drop_delegated_to_variable=true;
}
// Requires that the DropFields trait is implemented manually.
// DropFields = manual;
// This is an optional parameter,
// which determines how the FromStructural and TryFromStructural traits are delegated.
//
// If this parameter is not passed,
// then neither FromStructural and TryFromStructural is implemented.
FromStructural
// An optional where clause, with additional constraints
where [ u32: Copy, ]
{
// An optional parameter, defaults to the delegated-to type.
converted_from = T;
// The function that constructs this type from the `converted_from` type.
constructor = Foo::new;
}
}
impl<T> Foo<T>{
pub fn new(value: T)->Self{
Self{
a: (),
b: (),
c: (),
d: Bar{e: ()},
value,
}
}
}
§Example
This example is of a type wrapping a ManuallyDrop<T>,delegating to the T inside it.
use std::{
fmt::Debug,
mem::ManuallyDrop,
};
use structural::{StructuralExt,GetFieldMut,unsafe_delegate_structural_with,make_struct,fp};
struct Bar<T>{
value:ManuallyDrop<T>
}
impl<T> Bar<T>{
pub fn new(value:T)->Self{
Self{
value:ManuallyDrop::new(value)
}
}
}
unsafe_delegate_structural_with!{
// You must write a trailing comma.
impl[T,] Bar<T>
where[
// This requires T to implement Clone
// for `Bar<T>` to implement the accessor traits
T:Clone,
]
self_ident=this;
specialization_params(Sized);
delegating_to_type=T;
GetField {
&*this.value
}
GetFieldMut
where [T:Debug,]
{
&mut *this.value
}
as_delegating_raw{
&mut (*this).value as *mut ManuallyDrop<T> as *mut T
}
IntoField
where [T:Debug,]
{
ManuallyDrop::into_inner(this.value)
}
move_out_field{
&mut *this.value
}
DropFields = {
dropped_fields[]
}
}
{
let mut bar=Bar::new((2,3,5,8,13));
assert_eq!(
bar.fields(fp!(4,3,2,1,0)),
( &13, &8, &5, &3, &2 )
);
assert_eq!(
bar.fields_mut(fp!(1,2,3,4)),
( &mut 3, &mut 5, &mut 8, &mut 13 )
);
assert_eq!(bar.into_field(fp!(1)),3);
}
{
let mut bar=Bar::new(make_struct!{
#![derive(Clone,Debug)] //This derives Clone and Debug for the anonymous struct
a:"hello",
b:"world",
c:"tour",
});
assert_eq!(
bar.fields(fp!(a,b,c)),
( &"hello", &"world", &"tour" )
);
assert_eq!(
bar.fields_mut(fp!(c,b,a)),
( &mut"tour", &mut"world", &mut"hello" )
);
assert_eq!( bar.into_field(fp!(c)), "tour" );
}