Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add some traits to the stdlib #3796

Merged
merged 5 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions docs/docs/explanations/standard_library/traits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey sorry I'm a bit late for this PR, wondering if it would be worth to have some examples @jfecher ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created: #3969

title: Traits
description: Noir's stdlib provides a few commonly used traits.
keywords: [traits, trait, interface, protocol, default, add, eq]
---

## `std::default`

### `std::default::Default`

```rust
trait Default {
fn default() -> Self;
}
```

Constructs a default value of a type.

Implementations:
```rust
impl Default for Field { .. }

impl Default for i8 { .. }
impl Default for i16 { .. }
impl Default for i32 { .. }
impl Default for i64 { .. }

impl Default for u8 { .. }
impl Default for u16 { .. }
impl Default for u32 { .. }
impl Default for u64 { .. }

impl Default for () { .. }
impl Default for bool { .. }

impl<T, N> Default for [T; N]
where T: Default { .. }

impl<A, B> Default for (A, B)
where A: Default, B: Default { .. }

impl<A, B, C> Default for (A, B, C)
where A: Default, B: Default, C: Default { .. }

impl<A, B, C, D> Default for (A, B, C, D)
where A: Default, B: Default, C: Default, D: Default { .. }

impl<A, B, C, D, E> Default for (A, B, C, D, E)
where A: Default, B: Default, C: Default, D: Default, E: Default { .. }
```

For primitive integer types, the return value of `default` is `0`. Container
types such as arrays are filled with default values of their element type.

## `std::ops`

### `std::ops::Eq`

```rust
trait Eq {
fn eq(self, other: Self) -> bool;
}
```
Returns `true` if `self` is equal to `other`.

Implementations:
```rust
impl Eq for Field { .. }

impl Eq for i8 { .. }
impl Eq for i16 { .. }
impl Eq for i32 { .. }
impl Eq for i64 { .. }

impl Eq for u8 { .. }
impl Eq for u16 { .. }
impl Eq for u32 { .. }
impl Eq for u64 { .. }

impl Eq for () { .. }
impl Eq for bool { .. }

impl<T, N> Eq for [T; N]
where T: Eq { .. }

impl<A, B> Eq for (A, B)
where A: Eq, B: Eq { .. }

impl<A, B, C> Eq for (A, B, C)
where A: Eq, B: Eq, C: Eq { .. }

impl<A, B, C, D> Eq for (A, B, C, D)
where A: Eq, B: Eq, C: Eq, D: Eq { .. }

impl<A, B, C, D, E> Eq for (A, B, C, D, E)
where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. }
```

### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div`

These traits abstract over addition, subtraction, multiplication, and division respectively.
Although Noir does not currently have operator overloading, in the future implementing these
traits for a given type will also allow that type to be used with the corresponding operator
for that trait (`+` for Add, etc) in addition to the normal method names.

```rust
trait Add {
fn add(self, other: Self) -> Self;
}

trait Sub {
fn sub(self, other: Self) -> Self;
}

trait Mul {
fn mul(self, other: Self) -> Self;
}

trait Div {
fn div(self, other: Self) -> Self;
}
```

The implementations block below is given for the `Add` trait, but the same types that implement
`Add` also implement `Sub`, `Mul`, and `Div`.

Implementations:
```rust
impl Add for Field { .. }

impl Add for i8 { .. }
impl Add for i16 { .. }
impl Add for i32 { .. }
impl Add for i64 { .. }

impl Add for u8 { .. }
impl Add for u16 { .. }
impl Add for u32 { .. }
impl Add for u64 { .. }
```
48 changes: 48 additions & 0 deletions noir_stdlib/src/default.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
trait Default {
fn default() -> Self;
}

impl Default for Field { fn default() -> Field { 0 } }

impl Default for u8 { fn default() -> u8 { 0 } }
impl Default for u16 { fn default() -> u16 { 0 } }
impl Default for u32 { fn default() -> u32 { 0 } }
impl Default for u64 { fn default() -> u64 { 0 } }

impl Default for i8 { fn default() -> i8 { 0 } }
impl Default for i16 { fn default() -> i16 { 0 } }
impl Default for i32 { fn default() -> i32 { 0 } }
impl Default for i64 { fn default() -> i64 { 0 } }

impl Default for () { fn default() -> () { () } }
impl Default for bool { fn default() -> bool { false } }

impl<T, N> Default for [T; N] where T: Default {
fn default() -> [T; N] {
[T::default(); N]
}
}

impl<A, B> Default for (A, B) where A: Default, B: Default {
fn default() -> (A, B) {
(A::default(), B::default())
}
}

impl<A, B, C> Default for (A, B, C) where A: Default, B: Default, C: Default {
fn default() -> (A, B, C) {
(A::default(), B::default(), C::default())
}
}

impl<A, B, C, D> Default for (A, B, C, D) where A: Default, B: Default, C: Default, D: Default {
fn default() -> (A, B, C, D) {
(A::default(), B::default(), C::default(), D::default())
}
}

impl<A, B, C, D, E> Default for (A, B, C, D, E) where A: Default, B: Default, C: Default, D: Default, E: Default {
fn default() -> (A, B, C, D, E) {
(A::default(), B::default(), C::default(), D::default(), E::default())
}
}
2 changes: 2 additions & 0 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ mod compat;
mod option;
mod string;
mod test;
mod ops;
mod default;
mod prelude;

// Oracle calls are required to be wrapped in an unconstrained function
Expand Down
117 changes: 117 additions & 0 deletions noir_stdlib/src/ops.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

trait Add {
fn add(self, other: Self) -> Self;
}

impl Add for Field { fn add(self, other: Field) -> Field { self + other } }

impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } }
impl Add for u16 { fn add(self, other: u16) -> u16 { self + other } }
impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } }
impl Add for u64 { fn add(self, other: u64) -> u64 { self + other } }

impl Add for i8 { fn add(self, other: i8) -> i8 { self + other } }
impl Add for i16 { fn add(self, other: i16) -> i16 { self + other } }
impl Add for i32 { fn add(self, other: i32) -> i32 { self + other } }
impl Add for i64 { fn add(self, other: i64) -> i64 { self + other } }

trait Sub {
fn sub(self, other: Self) -> Self;
}

impl Sub for Field { fn sub(self, other: Field) -> Field { self - other } }

impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } }
impl Sub for u16 { fn sub(self, other: u16) -> u16 { self - other } }
impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } }
impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other } }

impl Sub for i8 { fn sub(self, other: i8) -> i8 { self - other } }
impl Sub for i16 { fn sub(self, other: i16) -> i16 { self - other } }
impl Sub for i32 { fn sub(self, other: i32) -> i32 { self - other } }
impl Sub for i64 { fn sub(self, other: i64) -> i64 { self - other } }

trait Mul {
fn mul(self, other: Self) -> Self;
}

impl Mul for Field { fn mul(self, other: Field) -> Field { self * other } }

impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } }
impl Mul for u16 { fn mul(self, other: u16) -> u16 { self * other } }
impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } }
impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other } }

impl Mul for i8 { fn mul(self, other: i8) -> i8 { self * other } }
impl Mul for i16 { fn mul(self, other: i16) -> i16 { self * other } }
impl Mul for i32 { fn mul(self, other: i32) -> i32 { self * other } }
impl Mul for i64 { fn mul(self, other: i64) -> i64 { self * other } }

trait Div {
fn div(self, other: Self) -> Self;
}

impl Div for Field { fn div(self, other: Field) -> Field { self / other } }

impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } }
impl Div for u16 { fn div(self, other: u16) -> u16 { self / other } }
impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } }
impl Div for u64 { fn div(self, other: u64) -> u64 { self / other } }

impl Div for i8 { fn div(self, other: i8) -> i8 { self / other } }
impl Div for i16 { fn div(self, other: i16) -> i16 { self / other } }
impl Div for i32 { fn div(self, other: i32) -> i32 { self / other } }
impl Div for i64 { fn div(self, other: i64) -> i64 { self / other } }

trait Eq {
fn eq(self, other: Self) -> bool;
}

impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } }

impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } }
impl Eq for u16 { fn eq(self, other: u16) -> bool { self == other } }
impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } }
impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } }

impl Eq for i8 { fn eq(self, other: i8) -> bool { self == other } }
impl Eq for i16 { fn eq(self, other: i16) -> bool { self == other } }
impl Eq for i32 { fn eq(self, other: i32) -> bool { self == other } }
impl Eq for i64 { fn eq(self, other: i64) -> bool { self == other } }

impl Eq for () { fn eq(_self: Self, _other: ()) -> bool { true } }
impl Eq for bool { fn eq(self, other: bool) -> bool { self == other } }

impl<T, N> Eq for [T; N] where T: Eq {
fn eq(self, other: [T; N]) -> bool {
let mut result = true;
for i in 0 .. self.len() {
result &= self[i].eq(other[i]);
}
result
}
}

impl<A, B> Eq for (A, B) where A: Eq, B: Eq {
fn eq(self, other: (A, B)) -> bool {
self.0.eq(other.0) & self.1.eq(other.1)
}
}

impl<A, B, C> Eq for (A, B, C) where A: Eq, B: Eq, C: Eq {
fn eq(self, other: (A, B, C)) -> bool {
self.0.eq(other.0) & self.1.eq(other.1) & self.2.eq(other.2)
}
}

impl<A, B, C, D> Eq for (A, B, C, D) where A: Eq, B: Eq, C: Eq, D: Eq {
fn eq(self, other: (A, B, C, D)) -> bool {
self.0.eq(other.0) & self.1.eq(other.1) & self.2.eq(other.2) & self.3.eq(other.3)
}
}

impl<A, B, C, D, E> Eq for (A, B, C, D, E) where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq {
fn eq(self, other: (A, B, C, D, E)) -> bool {
self.0.eq(other.0) & self.1.eq(other.1) & self.2.eq(other.2) & self.3.eq(other.3) & self.4.eq(other.4)
}
}
18 changes: 9 additions & 9 deletions test_programs/compile_failure/no_nested_impl/src/main.nr
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
fn main() {
let a: [[[[Field; 2]; 2]; 2]; 2] = [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]], [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]];
assert(a.eq(a));
assert(a.my_eq(a));
}

trait Eq {
fn eq(self, other: Self) -> bool;
trait MyEq {
fn my_eq(self, other: Self) -> bool;
}

impl<T> Eq for [T; 2] where T: Eq {
fn eq(self, other: Self) -> bool {
self[0].eq(other[0])
& self[0].eq(other[0])
impl<T> MyEq for [T; 2] where T: MyEq {
fn my_eq(self, other: Self) -> bool {
self[0].my_eq(other[0])
& self[0].my_eq(other[0])
}
}
// Impl for u32 but not Field
impl Eq for u32 {
fn eq(self, other: Self) -> bool {
impl MyEq for u32 {
fn my_eq(self, other: Self) -> bool {
self == other
}
}