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

chore(docs): document From and Into traits #4182

Merged
merged 4 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
11 changes: 6 additions & 5 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"bindgen",
"bitand",
"blackbox",
"boilerplate",
"boilerplates",
"bridgekeeper",
"brillig",
"bytecount",
Expand Down Expand Up @@ -56,6 +58,7 @@
"defunctionalized",
"deque",
"desugared",
"devcontainer",
"direnv",
"eddsa",
"Elligator",
Expand Down Expand Up @@ -96,6 +99,7 @@
"keccak",
"keccakf",
"krate",
"losslessly",
"lvalue",
"Maddiaa",
"mathbb",
Expand All @@ -121,6 +125,7 @@
"noirup",
"nomicfoundation",
"noncanonical",
"nouner",
"pedersen",
"peekable",
"plonkc",
Expand Down Expand Up @@ -172,11 +177,7 @@
"wasi",
"wasmer",
"Weierstraß",
"zshell",
"nouner",
"devcontainer",
"boilerplate",
"boilerplates"
"zshell"
],
"ignorePaths": [
"./**/node_modules/**",
Expand Down
35 changes: 35 additions & 0 deletions docs/docs/noir/standard_library/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,41 @@ impl<A, B, C, D, E> Default for (A, B, C, D, E)
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::convert`

### `std::convert::From`

#include_code from-trait noir_stdlib/src/convert.nr rust

The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented.

The Noir standard library provides a number of implementations of `From` between primitive types.
#include_code from-impls noir_stdlib/src/convert.nr rust

#### When to implement `From`

As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from):

- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead.
- The conversion is lossless: semantically, it should not lose or discard information. For example, `u32: From<u16>` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From<u32>` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`.
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From<u8>` to be implemented.
- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would not be ambiguous, resulting in two potential values of `U128` from the same byte array.
jfecher marked this conversation as resolved.
Show resolved Hide resolved
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved

One additional recommendation specific to Noir is:
- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided.

### `std::convert::Into`

The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`.

For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation.

#include_code into-trait noir_stdlib/src/convert.nr rust

`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question.
jfecher marked this conversation as resolved.
Show resolved Hide resolved


## `std::cmp`

### `std::cmp::Eq`
Expand Down
7 changes: 6 additions & 1 deletion noir_stdlib/src/convert.nr
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// docs:start:from-trait
trait From<T> {
fn from(input: T) -> Self;
}
// docs:end:from-trait

impl<T> From<T> for T {
fn from(input: T) -> T {
input
}
}

// docs:start:into-trait
trait Into<T> {
fn into(input: Self) -> T;
}
Expand All @@ -17,7 +20,9 @@ impl<T, U> Into<T> for U where T: From<U> {
T::from(input)
}
}
// docs:end:into-trait

// docs:start:from-impls
// Unsigned integers
impl From<u8> for u16 { fn from(value: u8) -> u16 { value as u16 } }

Expand Down Expand Up @@ -53,4 +58,4 @@ impl From<bool> for i16 { fn from(value: bool) -> i16 { value as i16 } }
impl From<bool> for i32 { fn from(value: bool) -> i32 { value as i32 } }
impl From<bool> for i64 { fn from(value: bool) -> i64 { value as i64 } }
impl From<bool> for Field { fn from(value: bool) -> Field { value as Field } }

// docs:end:from-impls
Loading