Documentation Index
Fetch the complete documentation index at: https://mintlify.com/noir-lang/noir/llms.txt
Use this file to discover all available pages before exploring further.
Traits in Noir are analogous to interfaces or protocols in other languages. A trait defines a set of method signatures that a type must implement. This allows functions to be generic over any type that satisfies certain behavior, without needing to know the concrete type in advance.
Defining a trait
trait Area {
fn area(self) -> Field;
}
Implementing a trait
Use impl TraitName for Type to provide concrete implementations:
struct Rectangle {
width: Field,
height: Field,
}
struct Triangle {
width: Field,
height: Field,
}
impl Area for Rectangle {
fn area(self) -> Field {
self.width * self.height
}
}
impl Area for Triangle {
fn area(self) -> Field {
self.width * self.height / 2
}
}
Using traits in generic functions
Add a where clause to constrain a generic type to implement a trait:
fn log_area<T>(shape: T) where T: Area {
println(shape.area());
}
This is equivalent to the inline bound syntax:
fn log_area<T: Area>(shape: T) {
println(shape.area());
}
Use where when there are many bounds and the separation improves readability.
Multiple bounds
Combine bounds with + and list multiple type constraints separated by commas:
fn foo<T, U>(elements: [T], thing: U) where
T: Default + Add + Eq,
U: Bar,
{
let mut sum = T::default();
for element in elements {
sum += element;
}
if sum == T::default() {
thing.bar();
}
}
Invoking trait methods
To call a trait method on a concrete type, the trait must be imported into scope:
use geometry::Rectangle;
use geometry::Area; // must import the trait
fn main() {
let rectangle = Rectangle { width: 1, height: 2 };
let area = rectangle.area(); // ok: uses Area::area
}
If two traits in scope define the same method name, use the fully qualified path:
use geometry::Rectangle;
fn main() {
let rectangle = Rectangle { width: 1, height: 2 };
let area = geometry::Area::area(rectangle);
}
As-trait syntax
When a generic type must call a method from a specific trait (e.g. when two traits define the same method name), use <Type as Trait>::method:
trait Foo { fn bar(); }
trait Foo2 { fn bar(); }
fn example<T>() where T: Foo + Foo2 {
<T as Foo>::bar();
<T as Foo2>::bar();
}
Default method implementations
A trait can provide a default body for any of its methods. Types only need to implement the required methods:
trait Numeric {
fn add(self, other: Self) -> Self;
// Default: double is implemented in terms of add
fn double(self) -> Self {
self.add(self)
}
}
impl Numeric for Field {
fn add(self, other: Field) -> Field {
self + other
}
// double is inherited automatically
}
impl Numeric for u32 {
fn add(self, other: u32) -> u32 {
self + other
}
// Override the default
fn double(self) -> u32 {
self * 2
}
}
Traits with no self parameter
Traits can define static methods (no self parameter). Call them via the type or the trait name:
trait Default {
fn default() -> Self;
}
impl Default for Field {
fn default() -> Field { 0 }
}
struct MyType {}
impl Default for MyType {
fn default() -> MyType { MyType {} }
}
fn main() {
let my_struct = MyType::default();
let x: u64 = Default::default();
let result = x + Default::default();
}
Generic trait implementations
Add generics after impl to implement a trait for a family of types:
trait Second {
fn second(self) -> Field;
}
impl<T> Second for (T, Field) {
fn second(self) -> Field {
self.1
}
}
You can implement a trait for all types:
trait Debug {
fn debug(self);
}
impl<T> Debug for T {
fn debug(self) {
println(self);
}
}
fn main() {
1.debug();
}
Add where clauses on implementations to restrict which generics qualify:
impl<T, let N: u32> Eq for [T; N] where T: Eq {
fn eq(self, other: Self) -> bool {
let mut result = true;
for i in 0..self.len() {
result &= self[i] == other[i];
}
result
}
}
Generic traits
Traits themselves can be generic. Specify the generic arguments when implementing or referencing the trait:
trait Into<T> {
fn into(self) -> T;
}
struct MyStruct {
array: [Field; 2],
}
impl Into<[Field; 2]> for MyStruct {
fn into(self) -> [Field; 2] {
self.array
}
}
fn as_array<T>(x: T) -> [Field; 2] where T: Into<[Field; 2]> {
x.into()
}
fn main() {
let array = [1, 2];
let my_struct = MyStruct { array };
assert_eq(as_array(my_struct), array);
}
Associated types and constants
Traits support associated types (type Foo) and associated constants (let Bar: u32):
trait MyTrait {
type Foo;
let Bar: u32;
}
impl MyTrait for Field {
type Foo = i32;
let Bar: u32 = 11;
}
// Specify associated items explicitly
fn foo<T>(x: T) where T: MyTrait<Foo = i32, Bar = 11> { ... }
// Or let the compiler infer them
fn bar<T>(x: T) where T: MyTrait { ... }
// Reference an associated type via as-trait syntax
fn baz<T>(x: T) where
T: MyTrait,
<T as MyTrait>::Foo: Default + Eq
{
let foo_value: <T as MyTrait>::Foo = Default::default();
assert_eq(foo_value, foo_value);
}
Impl specialization
Implement a trait for only a specific combination of generics:
trait Sub {
fn sub(self, other: Self) -> Self;
}
struct NonZero<T> {
value: T,
}
impl Sub for NonZero<Field> {
fn sub(self, other: Self) -> Self {
let value = self.value - other.value;
assert(value != 0);
NonZero { value }
}
}
Overlapping implementations
Overlapping impl blocks are disallowed to ensure unambiguous dispatch:
trait Trait {}
impl<A, B> Trait for (A, B) {}
// error: overlapping impl for (Field, Field)
// impl Trait for (Field, Field) {}
Trait coherence
To implement a trait, either the trait or the implementing type must be defined in the current crate. This prevents conflicts when using external libraries.
If you need to implement a foreign trait on a foreign type, use the newtype pattern:
struct Wrapper {
foo: some_library::Foo,
}
impl Default for Wrapper {
fn default() -> Wrapper {
Wrapper {
foo: some_library::Foo::new(),
}
}
}
Trait inheritance
A trait can require that implementors also implement other traits (supertraits):
trait Person {
fn name(self) -> String;
}
// Student requires Person to also be implemented
trait Student: Person {
fn university(self) -> String;
}
trait Programmer {
fn fav_language(self) -> String;
}
// CompSciStudent requires both Programmer and Student
trait CompSciStudent: Programmer + Student {
fn git_username(self) -> String;
}
Trait aliases
Trait aliases combine multiple traits under a single name:
trait Foo {
fn foo(self) -> Self;
}
trait Bar {
fn bar(self) -> Self;
}
trait Baz = Foo + Bar;
fn baz<T>(x: T) -> T where T: Baz {
x.foo().bar()
}
Generic trait aliases and where clauses are supported:
trait Bar<T> {
fn bar(self) -> T;
}
trait Baz<T> = Foo + Bar<T>;
trait Baz {
fn baz(self) -> bool;
}
// Qux<T> requires Foo + Bar<T> where T: Baz
trait Qux<T> = Foo + Bar<T> where T: Baz;
Visibility
Traits and trait aliases are private to their module by default. Use pub or pub(crate) to expose them:
pub trait Trait {}
pub trait Baz = Foo + Bar;
Operator overloading
Noir’s arithmetic and comparison operators are implemented via standard library traits. Implement these traits on your types to enable operator syntax:
Arithmetic
Equality and ordering
Default
Implement Add, Sub, Mul, Div, Rem from std::ops:use std::ops::Add;
struct Vec2 {
x: Field,
y: Field,
}
impl Add for Vec2 {
fn add(self, other: Vec2) -> Vec2 {
Vec2 { x: self.x + other.x, y: self.y + other.y }
}
}
fn main() {
let a = Vec2 { x: 1, y: 2 };
let b = Vec2 { x: 3, y: 4 };
let c = a + b; // uses Add::add
assert(c.x == 4);
assert(c.y == 6);
}
Implement Eq and Ord for comparison operators:struct MyStruct {
foo: Field,
}
impl Eq for MyStruct {
fn eq(self, other: MyStruct) -> bool {
self.foo == other.foo
}
}
Implement Default to create a zero/empty value:impl Default for Field {
fn default() -> Field {
0
}
}
fn main() {
let x: Field = Default::default();
assert(x == 0);
}