Metaprogramming
std::meta
is the entry point for Noir's metaprogramming API. This consists of comptime
functions
and types used for inspecting and modifying Noir programs.
Functions
type_of
pub comptime fn type_of<T>(x: T) -> Type {}
Returns the type of a variable at compile-time.
Example:
comptime {
let x: i32 = 1;
let x_type: Type = std::meta::type_of(x);
assert_eq(x_type, quote { i32 }.as_type());
}
unquote
pub comptime fn unquote(code: Quoted) -> Quoted {
Unquotes the passed-in token stream where this function was called.
Example:
comptime {
let code = quote { 1 + 2 };
// let x = 1 + 2;
let x = unquote!(code);
}
derive
#[varargs]
pub comptime fn derive(s: StructDefinition, traits: [TraitDefinition]) -> Quoted {
Attribute placed on struct definitions.
Creates a trait impl for each trait passed in as an argument.
To do this, the trait must have a derive handler registered
with derive_via
beforehand. The traits in the stdlib that
can be derived this way are Eq
, Ord
, Default
, and Hash
.
Example:
#[derive(Eq, Default)]
struct Foo<T> {
x: i32,
y: T,
}
fn main() {
let foo1 = Foo::default();
let foo2 = Foo { x: 0, y: &[0] };
assert_eq(foo1, foo2);
}
derive_via
pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) {
Attribute placed on trait definitions.
Registers a function to create impls for the given trait
when the trait is used in a derive
call. Users may use
this to register their own functions to enable their traits
to be derived by derive
.
Because this function requires a function as an argument which
should produce a trait impl for any given struct, users may find
it helpful to use a function like std::meta::make_trait_impl
to
help creating these impls.
Example:
#[derive_via(derive_do_nothing)]
trait DoNothing {
fn do_nothing(self);
}
comptime fn derive_do_nothing(s: StructDefinition) -> Quoted {
let typ = s.as_type();
quote {
impl DoNothing for $typ {
fn do_nothing(self) {
println("Nothing");
}
}
}
}
As another example, derive_eq
in the stdlib is used to derive the Eq
trait for any struct. It makes use of make_trait_impl
to do this:
comptime fn derive_eq(s: StructDefinition) -> Quoted {
let signature = quote { fn eq(_self: Self, _other: Self) -> bool };
let for_each_field = |name| quote { (_self.$name == _other.$name) };
let body = |fields| {
if s.fields().len() == 0 {
quote { true }
} else {
fields
}
};
crate::meta::make_trait_impl(
s,
quote { Eq },
signature,
for_each_field,
quote { & },
body,
)
}
make_trait_impl
pub comptime fn make_trait_impl<Env1, Env2>(
s: StructDefinition,
trait_name: Quoted,
function_signature: Quoted,
for_each_field: fn[Env1](Quoted) -> Quoted,
join_fields_with: Quoted,
body: fn[Env2](Quoted) -> Quoted,
) -> Quoted {
A helper function to more easily create trait impls while deriving traits.
Note that this function only works for traits which:
- Have only one method
- Have no generics on the trait itself.
- E.g. Using this on a trait such as
trait Foo<T> { ... }
will result in the generated impl incorrectly missing theT
generic.
If your trait fits these criteria then make_trait_impl
is likely the easiest
way to write your derive handler. The arguments are as follows:
s
: The struct to make the impl fortrait_name
: The name of the trait to derive. E.g.quote { Eq }
.function_signature
: The signature of the trait method to derive. E.g.fn eq(self, other: Self) -> bool
.for_each_field
: An operation to be performed on each field. E.g.|name| quote { (self.$name == other.$name) }
.join_fields_with
: A separator to join each result offor_each_field
with. E.g.quote { & }
. You can also use an emptyquote {}
for no separator.body
: The result of the field operations is passed into this function for any final processing. This is the place to insert any setup/teardown code the trait requires. If the trait doesn't require any such code, you can return the body as-is:|body| body
.
Example deriving Hash
:
comptime fn derive_hash(s: StructDefinition) -> Quoted {
let name = quote { Hash };
let signature = quote { fn hash<H>(_self: Self, _state: &mut H) where H: std::hash::Hasher };
let for_each_field = |name| quote { _self.$name.hash(_state); };
crate::meta::make_trait_impl(
s,
name,
signature,
for_each_field,
quote {},
|fields| fields,
)
}
Example deriving Ord
:
comptime fn derive_ord(s: StructDefinition) -> Quoted {
let signature = quote { fn cmp(_self: Self, _other: Self) -> std::cmp::Ordering };
let for_each_field = |name| quote {
if result == std::cmp::Ordering::equal() {
result = _self.$name.cmp(_other.$name);
}
};
let body = |fields| quote {
let mut result = std::cmp::Ordering::equal();
$fields
result
};
crate::meta::make_trait_impl(s, quote { Ord }, signature, for_each_field, quote {}, body)
}