121 lines
3.4 KiB
Markdown
121 lines
3.4 KiB
Markdown
{{#include ../links.md}}
|
|
|
|
# Handling Data
|
|
|
|
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
|
|
methods perform three different, but related, functions, and it's important to
|
|
choose the correct name when writing methods. These methods take data that are
|
|
stored inside of the struct, and return them in some manner. For example, let's
|
|
look at the `Mnemonic` struct:
|
|
|
|
```rust
|
|
pub struct Mnemonic {
|
|
entropy: Vec<u8>,
|
|
// fields omitted
|
|
}
|
|
```
|
|
|
|
We're going to define the three methods `as_bytes()`, `to_bytes()`, and
|
|
`into_bytes()`, explaining what the details of each are. To start with:
|
|
|
|
### `Mnemonic::as_bytes()`
|
|
|
|
This method will return a `&[u8]`, a reference to a slice of bytes, without
|
|
allocating anything. The `Mnemonic` type is not consumed by this process.
|
|
|
|
```rust
|
|
impl Mnemonic {
|
|
/// Return a slice of bytes represented by the mnemonic.
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
&self.entropy
|
|
}
|
|
}
|
|
```
|
|
|
|
### `Mnemonic::to_bytes()`
|
|
|
|
This method will return a `Vec<u8>`, an allocated type containing the bytes,
|
|
without consuming the `Mnemonic` type. This is useful if you may need the
|
|
`Mnemonic` type in the future.
|
|
|
|
```rust
|
|
impl Mnemonic {
|
|
/// Return a `Vec<u8>` of bytes represented by the mnemonic.
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
self.entropy.to_vec()
|
|
}
|
|
}
|
|
```
|
|
|
|
### `Mnemonic::into_bytes()`
|
|
|
|
This method will return the internal representation of bytes. No allocation is
|
|
performed, but the `Mnemonic` value is no longer accessible.
|
|
|
|
```rust
|
|
impl Mnemonic {
|
|
/// Return a Vec<u8> of bytes represented by the mnemonic.
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
self.entropy
|
|
}
|
|
}
|
|
```
|
|
|
|
## Creating Data: `Mnemonic::seed()`
|
|
|
|
Sometimes you may have an operation (such as creating a seed from a mnemonic)
|
|
which can't possibly happen without allocating some kind of data. In that case,
|
|
it is not reasonable - or even possible - to have three methods for that case.
|
|
In that case, it is reasonable to have a method with a "bare" name, such as
|
|
`Mnemonic::seed()`. Additionally, unless it makes sense to make the type
|
|
unusable after such an operation (using the type system to your advantage), it
|
|
makes sense to borrow from self while returning the allocated value.
|
|
|
|
```rust
|
|
impl Mnemonic {
|
|
/// Create a seed from the mnemonic's data.
|
|
pub fn seed(&self) -> Result<Vec<u8>, Error> {
|
|
let result = perform_secret_operation(self.entropy)?;
|
|
Ok(result)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Complex Example: `Mnemonic::into_parts()`
|
|
|
|
Let's add a `Wordlist` to our `Mnemonic` type, so we can format it as a string.
|
|
|
|
```rust
|
|
pub struct Mnemonic {
|
|
entropy: Vec<u8>,
|
|
wordlist: Wordlist,
|
|
}
|
|
```
|
|
|
|
Because we now have two expensive-to-allocate variables in our type, we need to
|
|
change how we deconstruct it into parts:
|
|
|
|
```rust
|
|
impl Mnemonic {
|
|
/// Split the Mnemonic into its interior parts.
|
|
pub fn into_parts(self) -> (Vec<u8>, Wordlist) {
|
|
let Mnemonic { entropy, wordlist } = self;
|
|
(entropy, wordlist)
|
|
}
|
|
|
|
/// Return a Vec<u8> of bytes represented by the mnemonic.
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
self.into_parts().0
|
|
}
|
|
|
|
/// Return the Wordlist used by the mnemonic.
|
|
pub fn into_wordlist(self) -> Wordlist {
|
|
self.into_parts().1
|
|
}
|
|
}
|
|
```
|
|
|
|
In practical use, `Mnemonic` stores `wordlist` as an `Arc<T>` to allow reusing
|
|
the same wordlist (parsing it is expensive!), but we'll ignore that for
|
|
demonstration purposes.
|