1"use strict";(globalThis.webpackChunkdocs=globalThis.webpackChunkdocs||[]).push([[2525],{48481(e,n,r){r.r(n),r.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"libraries/standard_library/meta/index","title":"Metaprogramming","description":"Noir\'s Metaprogramming API","source":"@site/processed-docs/libraries/standard_library/meta/index.md","sourceDirName":"libraries/standard_library/meta","slug":"/libraries/standard_library/meta/","permalink":"/docs/dev/libraries/standard_library/meta/","draft":false,"unlisted":false,"editUrl":"https://github.com/noir-lang/noir/edit/master/docs/docs/libraries/standard_library/meta/index.md","tags":[],"version":"current","frontMatter":{"title":"Metaprogramming","description":"Noir\'s Metaprogramming API","keywords":["metaprogramming","comptime","macros","macro","quote","unquote"]},"sidebar":"sidebar","previous":{"title":"Memory Module","permalink":"/docs/dev/libraries/standard_library/mem"},"next":{"title":"CtString","permalink":"/docs/dev/libraries/standard_library/meta/ctstring"}}');var t=r(74848),s=r(28453);const o={title:"Metaprogramming",description:"Noir's Metaprogramming API",keywords:["metaprogramming","comptime","macros","macro","quote","unquote"]},a=void 0,l={},d=[{value:"Functions",id:"functions",level:2},{value:"type_of",id:"type_of",level:3},{value:"unquote",id:"unquote",level:3},{value:"error",id:"error",level:3},{value:"warn",id:"warn",level:3},{value:"derive",id:"derive",level:3},{value:"derive_via",id:"derive_via",level:3},{value:"make_trait_impl",id:"make_trait_impl",level:3}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",sub:"sub",sup:"sup",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.code,{children:"std::meta"})," is the entry point for Noir's metaprogramming API. This consists of ",(0,t.jsx)(n.code,{children:"comptime"})," functions\nand types used for inspecting and modifying Noir programs."]}),"\n",(0,t.jsx)(n.h2,{id:"functions",children:"Functions"}),"\n",(0,t.jsx)(n.h3,{id:"type_of",children:"type_of"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="type_of" showLineNumbers ',children:"pub comptime fn type_of<T>(x: T) -> Type {}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L32-L34",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L32-L34"})})})}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"Returns the type of a variable at compile-time."}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:"comptime {\n let x: i32 = 1;\n let x_type: Type = std::meta::type_of(x);\n\n assert_eq(x_type, quote { i32 }.as_type());\n}\n"})}),"\n",(0,t.jsx)(n.h3,{id:"unquote",children:"unquote"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="unquote" showLineNumbers ',children:"pub comptime fn unquote(code: Quoted) -> Quoted {\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L24-L26",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L24-L26"})})})}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"Unquotes the passed-in token stream where this function was called."}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:"comptime {\n let code = quote { 1 + 2 };\n\n // let x = 1 + 2;\n let x = unquote!(code);\n}\n"})}),"\n",(0,t.jsx)(n.h3,{id:"error",children:"error"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="error" showLineNumbers ',children:"pub comptime fn error<let N: u32, T, let N2: u32, T2>(\n _msg: fmtstr<N, T>,\n _secondary: Option<fmtstr<N2, T2>>,\n _location: Location,\n) {}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L40-L46",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L40-L46"})})})}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Issues an error diagnostic at the given ",(0,t.jsx)(n.a,{href:"/docs/dev/libraries/standard_library/meta/location",children:(0,t.jsx)(n.code,{children:"Location"})})," with the given primary message\nand an optional secondary message. Unlike ",(0,t.jsx)(n.code,{children:"panic"}),", the comptime interpreter continues executing\nafter ",(0,t.jsx)(n.code,{children:"error"})," is called, so multiple errors can be reported from a single attribute or comptime block."]}),"\n",(0,t.jsx)(n.p,{children:"Compilation will still fail at the end of elaboration if any errors were issued."}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:'use std::meta::error;\n\n#[reject]\nfn forbidden() {}\n\ncomptime fn reject(f: FunctionDefinition) {\n error(\n f"`{f}` may not be called",\n Option::some(f"see the migration guide for an alternative"),\n f.location(),\n );\n}\n'})}),"\n",(0,t.jsx)(n.h3,{id:"warn",children:"warn"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="warn" showLineNumbers ',children:"pub comptime fn warn<let N: u32, T, let N2: u32, T2>(\n _msg: fmtstr<N, T>,\n _secondary: Option<fmtstr<N2, T2>>,\n _location: Location,\n) {}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L52-L58",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L52-L58"})})})}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Issues a warning diagnostic at the given ",(0,t.jsx)(n.a,{href:"/docs/dev/libraries/standard_library/meta/location",children:(0,t.jsx)(n.code,{children:"Location"})})," with the given primary message\nand an optional secondary message. C
1ompilation continues normally after the warning is reported."]}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:'use std::meta::warn;\n\n#[deprecated_notice]\nfn old_api() {}\n\ncomptime fn deprecated_notice(f: FunctionDefinition) {\n warn(\n f"`{f}` is deprecated",\n Option::some(f"prefer the new replacement"),\n f.location(),\n );\n}\n'})}),"\n",(0,t.jsx)(n.h3,{id:"derive",children:"derive"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="derive" showLineNumbers ',children:"#[varargs]\npub comptime fn derive(s: TypeDefinition, traits: [TraitDefinition]) -> Quoted {\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L77-L80",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L77-L80"})})})}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"Attribute placed on type definitions."}),"\n",(0,t.jsxs)(n.p,{children:["Creates a trait impl for each trait passed in as an argument.\nTo do this, the trait must have a derive handler registered\nwith ",(0,t.jsx)(n.code,{children:"derive_via"})," beforehand. The traits in the stdlib that\ncan be derived this way are ",(0,t.jsx)(n.code,{children:"Eq"}),", ",(0,t.jsx)(n.code,{children:"Ord"}),", ",(0,t.jsx)(n.code,{children:"Default"}),", and ",(0,t.jsx)(n.code,{children:"Hash"}),"."]}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:"#[derive(Eq, Default)]\nstruct Foo<T> {\n x: i32,\n y: T,\n}\n\nfn main() {\n let foo1 = Foo::default();\n let foo2 = Foo { x: 0, y: @[0] };\n assert_eq(foo1, foo2);\n}\n"})}),"\n",(0,t.jsx)(n.h3,{id:"derive_via",children:"derive_via"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="derive_via_signature" showLineNumbers ',children:"pub comptime fn derive_via(t: TraitDefinition, f: DeriveFunction) {\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L97-L99",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L97-L99"})})})}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"Attribute placed on trait definitions."}),"\n",(0,t.jsxs)(n.p,{children:["Registers a function to create impls for the given trait\nwhen the trait is used in a ",(0,t.jsx)(n.code,{children:"derive"})," call. Users may use\nthis to register their own functions to enable their traits\nto be derived by ",(0,t.jsx)(n.code,{children:"derive"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["Because this function requires a function as an argument which\nshould produce a trait impl for any given type definition, users may find\nit helpful to use a function like ",(0,t.jsx)(n.code,{children:"std::meta::make_trait_impl"})," to\nhelp creating these impls."]}),"\n",(0,t.jsx)(n.p,{children:"Example:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",children:'#[derive_via(derive_do_nothing)]\ntrait DoNothing {\n fn do_nothing(self);\n}\n\ncomptime fn derive_do_nothing(s: TypeDefinition) -> Quoted {\n let typ = s.as_type();\n quote {\n impl DoNothing for $typ {\n fn do_nothing(self) {\n println("Nothing");\n }\n }\n }\n}\n'})}),"\n",(0,t.jsxs)(n.p,{children:["As another example, ",(0,t.jsx)(n.code,{children:"derive_eq"})," in the stdlib is used to derive the ",(0,t.jsx)(n.code,{children:"Eq"}),"\ntrait for any type definition. It makes use of ",(0,t.jsx)(n.code,{children:"make_trait_impl"})," to do this:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="derive_eq" showLineNumbers ',children:"comptime fn derive_eq(s: TypeDefinition) -> Quoted {\n let signature = quote { fn eq(_self: Self, _other: Self) -> bool };\n let for_each_field = |name| quote { (_self.$name == _other.$name) };\n let body = |fields| {\n if s.fields_as_written().len() == 0 {\n quote { true }\n } else {\n fields\n }\n };\n crate::meta::make_trait_impl(\n s,\n quote { $crate::cmp::Eq },\n signature,\n for_each_field,\n quote { & },\n body,\n )\n}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/cmp.nr#L12-L32",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/cmp.nr#L12-L32"})})})}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"make_trait_impl",children:"make_trait_impl"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="make_trait_impl" showLineNumbers ',children:"pub comptime fn make_trait_impl<Env1, Env2>(\n s: TypeDefinition,\n trait_name: Quoted,\n function_signature: Quoted,\n for_each_field: fn[Env1](Quoted) -> Quoted,\n join_fields_with: Quoted,\n body: fn[Env2](Quoted) -> Quoted,\n) -> Quoted {\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/meta/mod.nr#L116-L125",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/meta/mod.nr#L116-L125"})})})}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"A helper function to more easily create trait impls while deriving traits."}),"\n",(0,t.jsx)(n.p,{children:"Note that this function only works for traits which:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Have only one method"}),"\n",(0,t.jsx)(n.li,{children:"Have no generics on the trait itself."}),"\n"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["E.g. Using this on a trait such as ",(0,t.jsx)(n.code,{children:"trait Foo<T> { ... }"})," will result in the\ngenerated impl incorrectly missing the ",(0,t.jsx)(n.code,{children:"T"})," generic."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["If your trait fits these criteria then ",(0,t.jsx)(n.code,{children:"make_trait_impl"})," is likely the easiest\nway to write your derive handler. The arguments are as follows:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"s"}),": The type definition to make the impl for"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"trait_name"}),": The name of the trait to derive. E.g. ",(0,t.jsx)(n.code,{children:"quote { Eq }"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"function_signature"}),": The signature of the trait method to derive. E.g. ",(0,t.jsx)(n.code,{children:"fn eq(self, other: Self) -> bool"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"for_each_field"}),": An operation to be performed on each field. E.g. ",(0,t.jsx)(n.code,{children:"|name| quote { (self.$name == other.$name) }"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"join_fields_with"}),": A separator to join each result of ",(0,t.jsx)(n.code,{children:"for_each_field"})," with.\nE.g. ",(0,t.jsx)(n.code,{children:"quote { & }"}),". You can also use an empty ",(0,t.jsx)(n.code,{children:"quote {}"})," for no separator."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"body"}),": The result of the field operations is passed into this function for any final processing.\nThis is the place to insert any setup/teardown code the trait requires. If the trait doesn't require\nany such code, you can return the body as-is: ",(0,t.jsx)(n.code,{children:"|body| body"}),"."]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Example deriving ",(0,t.jsx)(n.code,{children:"Hash"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="derive_hash" showLineNumbers ',children:"comptime fn derive_hash(s: TypeDefinition) -> Quoted {\n let name = quote { $crate::hash::Hash };\n let signature = quote { fn hash<H>(_self: Self, _state: &mut H) where H: $crate::hash::Hasher };\n let for_each_field = |name| quote { _self.$name.hash(_state); };\n crate::meta::make_trait_impl(\n s,\n name,\n signature,\n for_each_field,\n quote {},\n |fields| fields,\n )\n}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/hash/mod.nr#L138-L152",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/hash/mod.nr#L138-L152"})})})}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Example deriving ",(0,t.jsx)(n.code,{children:"Ord"}),":"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-rust",metastring:'title="derive_ord" showLineNumbers ',children:"comptime fn derive_ord(s: TypeDefinition) -> Quoted {\n let name = quote { $crate::cmp::Ord };\n let signature = quote { fn cmp(_self: Self, _other: Self) -> $crate::cmp::Ordering };\n let for_each_field = |name| quote {\n if result == $crate::cmp::Ordering::equal() {\n result = _self.$name.cmp(_other.$name);\n }\n };\n let body = |fields| quote {\n let mut result = $crate::cmp::Ordering::equal();\n $fields\n result\n };\n crate::meta::make_trait_impl(s, name, signature, for_each_field, quote {}, body)\n}\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.sup,{children:(0,t.jsx)(n.sub,{children:(0,t.jsx)(n.a,{href:"https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/cmp.nr#L253-L269",target:"_blank",rel:"noopener noreferrer",children:"Source code: noir_stdlib/src/cmp.nr#L253-L269"})})})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(c,{...e})}):c(e)}},28453(e,n,r){r.d(n,{R:()=>o,x:()=>a});var i=r(96540);const t={},s=i.createContext(t);function o(e){const n=i.useContext(s);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),i.createElement(s.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.