The Basics of Rust Structs

[caption id=“attachment_1026” align=“alignright” width=“300”]Pretty structs, as far as the eye can see… Pretty structs, as far as the eye can see…[/caption] structs are one of the basic building blocks in Rust. A struct lets us create more complex data types of our own. Instead of having to jump through naming hoops to make our own data structures, we can create whatever we want.

Our First Rust struct

Let’s look at creating a simple struct for a to do application. Why a todo application? Because it’s simple and I’m writing this, not you. Our struct will look something like this:pub struct Todo { title: String, description: String, // We're using seconds since epoch to avoid bringing in additional // dependencies on an external library and making this more complex than // it needs to be. created\_at\_s: i64, completed\_at\_s: i64 }This is a simple struct. The Todo describes a single item on a todo list - it has a name, a description, and timestamps to store when something was created and completed. Those last two are for tracking how lazy we are. We’ve made the struct public with the pub modifier since we might want to use it in a different module of our program.

struct Field Visibility in Rust

There’s a problem with our struct - by default struct fields are private. Other parts of our program won’t be able to read the title or description, they’ll just know that we’ve got a pile of information. We need to fix that!pub struct Todo {     pub title: String,     pub description: String,     pub created\_at\_s: i64, pub completed\_at\_s: i64 }Now people can read everything inside the struct! That’s probably not the best idea, though, because it’s coupling the way we store time (in seconds since January 1, 1970) with the way users work with that time. What if we decide we need more precision in the future? Or if we want to change the beginning of our epoch? People using our Todo would have to go through and change all of their code. What if those poor fools didn’t know that we meant seconds since January 1, 1970 in the Pacific time zone instead of UTC, as is the norm? EVERYTHING IS HORRIBLE! Thankfully, we can fix this. We’ll leave off the pub modifier from the two time-like fields. Our Todo now looks like:``` pub struct Todo {     pub title: String,     pub description: String,     created_at_s: i64, completed_at_s: i64 }


### Adding Methods to a `struct`

We need to give our `Todo` some data access methods to make life easier for people working with it. To create methods for the struct, we have to implement them. To do that, we add an `impl` block that references the name of the struct:```
impl Todo {
    fn created\_at(&self) -> Tm {
        time::at(Timespec::new(self.created\_at, 0))
    }
    
    fn completed\_at(&self) -> Tm {
        time::at(Timespec::new(self.completed\_at, 0))
    }
}
```Wait a minute... What if we haven't completed a `Todo`? Right now, we need to have a value present in the `completed_at` field. Rust doesn't allow for null values, so we need some way to indicate that there may or may not be something present. From [Error Handling in Rust](https://facility9.com/2016/03/error-handling-in-rust/), you might recall that we can use `Option` to represent the possibility of nothing. Back to the struct we go:```
pub struct Todo {
    pub title: String,
    pub description: String, 
    created\_at\_s: i64,
    completed\_at\_s: Option<i64>
}
```Now we can re-write the `completed_at` method:```
fn completed\_at(&self) -> Option<Tm> {
    match self.completed\_at\_s {
        None => None,
        Some(t) => Some(time::at(Timespec::new(t, 0))),
    }
}
```Hooray! Now we can see what the time of completion is or get back a `None` if the task hasn't been completed yet. We can even make this more concise and get rid of the `match` altogether:```
fn completed\_at(&self) -> Option<Tm> {
    self.completed\_at\_s.map(|t| time::at(Timespec::new(t,0)))
}

Creating Our struct

Rust makes it easy to construct a new struct using initializer syntax:let t = Todo { title: "hooray".to\_string(),                description: "do stuff".to\_string(),                created\_at\_s: 0,                completed\_at\_s: None };That’s ugly; it also looks a lot like JavaScript’s initializer syntax. We don’t want to have to type all of that out every time we make a new Todo. Plus, this lets someone write the data inside the Todo however they want. We don’t want that to happen at all! Let’s add a function to make a new Todo. Inside the impl we’ll add this:pub fn new(title: String, description: String) -> Todo { Todo { title: title,            description: description,            created\_at\_s: time::now\_utc().to\_timespec().sec,            completed\_at\_s: None } }Now when we want to create a new Todo struct we can just execute let t = Todo::new(my_title, my_description); and we’ll get the a brand new chunk of data with the creation timestamp set to the current moment in UTC time.

Where is our struct now?

At this point, we’ve got a lot of code that we could use. Check it out: https://gist.github.com/peschkaj/51a6da274b089f4c978791dcaf58a953 If we executed the code, we should see something like:Title: hooray Description: do stuff Created at: 1460394846 Created at (pretty): Mon, 11 Apr 2016 10:14:06We’ve created a basic struct for a Todo application. We’ve added functions that can manipulate that struct. We’ve even implemented a basic example of how to work with the struct. Working with basic data structures in Rust is simple and easy. We create our data structure as a struct. If we want to do work on the data, we can use impl implement functions that will manipulate the data directly. To read more about how to work with structs in Rust, check out the Rust book’s sections on Structs, Method Syntax, and the language reference about implementations.