The Internet of Things (IoT) is a highly encompassing term that covers everything from your home network-connected camera to the oven that is Wi-Fi connected, all the way to your modern electric cars like the Tesla that are always connected to the network and almost always on. The most basic premise of IoT is a hardware device that is a connected network appliance. In modern days, that usually means Internet and almost always connected to a cloud service, but it can just as easily be a local area network.
Only in the last 10 years have we truly embraced the IoT model for not only offices and factories but for everyday living. The most common consumer IoT systems are the ones from or supported by Apple, Google, and Amazon that provide cameras, thermostats, doorbell, and lights. All of those devices can then be used in conjunction with each other and for home automation and control. While many of these devices are used for fun in a home, they have beneficial application for elderly care and for medical monitoring and even can be used in industrial and manufacturing components. Devices in factories can report on the status of how many components are rolling off the assembly line, if there is a failure at a point, or even throughput of a factory. Used in conjunction with machine learning, the possibilities are endless.
And while IoT as a term didn’t make our way officially in the lexicon till 1999 by Kevin Ashton of Procter & Gamble, the concept has been around since well before that. What gave birth to IoT dates back to 1959 with the Egyptian-born Inventor Mohamed M. Atalla and Korean-born Dawon Kahng while working at Bell Labs in 1959. They created the MOSFET (metal-oxide-semiconductor field-effect transmitter) which is the basis for the semiconductor, which revolutionized electronics from huge tubes to the microchip components we have in our smart watches, phones, cameras, cars, and even your ovens. It would still take another 23 years though till someone at Carnegie Mellon decided to hook up monitoring a Coca-Cola machine for its inventory that would mark the first true IoT device, before anyone even thought of what IoT was, and then another 10 years before companies like Microsoft and Novell really proposed usable solutions. However, even then chips were expensive and relatively big. Today Raspberry Pi packs way more punch than the desktops of the 1990s, especially in the GPU department.
Who Is This Book For?
This book is for anyone from your hobbyist to someone trying to create their own commercial IoT products. I guess for your hobbyist, there is the question of why. Purchasing IoT applications has become inexpensive, fairly customizable, and routine; why bother to create your own? And while one answer is simply for fun, another is that you want to create a fully customizable solution. And finally another answer that became even more apparent this year was ownership, that you are the sole owner. This importance became obvious to me in two cases this year. This first was with the Amazon-owned company Ring. They had had to let go of four employees due to privacy concerns that they had spied on customers snooping in on their feeds. And while this is likely the exception and not the rule, it still lends to the idea of wanting to create pipes that are 100% solely owned by you.1 The second was Sonos, who after customers spent years buying components found out the older products will no longer be backward compatible, leaving many in the dark to use new software updates.2 And while it would be hard to replicate the amount of code they write, the open source community that integrates with custom Pi components is growing and will help to live on even if it means you have to code it yourself.
This book is titled Rust for the IoT. Before we discuss what we are going to build, let’s break out those two words further.
What Is IoT?
Internet of Things, or IoT, which will be used to reference it for the rest of the book, has become a new and ever-growing marketplace in the last few years, even though it’s been around for decades. At its core, it’s a network of devices that communicate with each other and often with the cloud.
The most common IoT systems are the ones from or supported by Apple, Google, and Amazon that provide cameras, thermostats, doorbell, and lights that all interact with each other. These devices in conjunction can be used in home automation and control. While many of these devices are used for fun in a home, they have beneficial application for elderly care and for medical monitoring and even can be used in industrial and manufacturing components.
In addition, IoT does not stop there; it’s gained dominance in all realms of device use. Car companies have started adopting IoT to have a more fully connected car. Tesla started the trend, and others have really picked it up full speed and use the same concepts and features as your smart device. Incidentally, this is something I know quite a bit about because I was in charge of architecting and coding Over-The-Air (OTA) updates for one such car company.
For this book though, I am sticking to personal use in the home, since most people into IoT are home enthusiasts and because creating one for a car is a tad more expensive since you would need a car. But the same principles can be applied everywhere.
I have had an interest in IoT since the rudimentary RF devices you could purchase in the 1980s from RadioShack. Quite a bit has changed since then. We are now in an age where home automation is actually pretty good. We have cameras, devices, cloud, and voice integration, but there are still many improvements to be made. We feel this book could start you on your way as a hobbyist or even in a professional setting. Why Rust? When reviewing what languages we could use for both embedded board development and was extremely fast cloud throughput computing at a low cost, Rust kept coming up.
IoT 10K Foot Picture
Let’s get a few takeaways from this diagram. You will notice at the bottom there are a few hardware devices and a mobile application. Hardware devices in our case will be a Raspberry Pi, but they could just as easily be your Google Home Hub, Alexa, or a car. These are all devices you are familiar with. The Raspberry Pi and Google Home Hub in the picture serve as endpoints that can play music, capture video, or record other information about their surroundings. The mobile devices then serve a role in communicating with those devices (in the case of the Google Home Hub, it serves a dual role, one in communicating and the other in capturing the world around it).
The end goal as we said is to have a fully connected system, so not only do these devices communicate with the cloud, but they receive communications back from the cloud. The communication back from the cloud can be due to input from your mobile application or could be a scheduled call. The pipes between represent this communication, but you will notice we have a variety of communication paths listed.
HTTPS
This is your standard HTTPS path. These paths exist often from the device to the cloud. Remember the endpoint in your cloud will have a static domain name like rustfortheiot.xyz. This domain name allows a constant path for the IoT device to talk to. The device can upload video or other large data and can download video, music, and other media content. And it’s also available for anything that would require an immediate request/response, for example, if we wanted to know what the forecast was for today.
The downside to HTTPS connections is that if the server endpoints are down, if they are overloaded, they may be slow or not responsive at all. In addition, there is data that the device will send back that doesn’t require a response.
The hardware is the core feature the reason we even have the rest of the diagram. These will give life to our commands. A car every time you drive is generating data on your speed, distance, and so on. Your home devices know when you turn on the lights and when you walk by a camera even if it’s not recording; it’s detecting the motion. For that data, HTTP may not even work or is overkill.
Message Queue
Message queues (MQs) you have often used with any publication/subscriber system and that in many ways are a few of the use cases we just described. If you are sending health data of your device, periodical temperature readings, this is all pub/sub type systems. The device wants to send the data to the cloud, but it doesn’t care where the data eventually ends. MQs are battle tested to handle high loads and are not as often updated as your microservice updates. This means you can easily update your microservices without worrying about downtime of the application. In addition, if you need to take the microservice down for an extended time, you won’t lose the data; it will receive it once it reconnects to the message queue.
We will use the message queue as the intermediary for sending messages back and then the HTTPS call from the hardware for retrieving videos. Also remember that the calls you will be making for HTTPS will be secure connections, and the MQ calls should be via Transport Layer Security (TLS) . Now let’s jump to the cloud. You will notice a fairly standard application layer with microservices, a database, and a bucket to store files in. In our case, we will used a local store for saving image and video files. Two other interesting items are the message queue (MQ) and machine learning. Machine learning (ML) is growing and really helps with IoT devices since often they generate so much data. We just mentioned all the data that the MQ can retrieve. This data is invaluable in being able to use ML to generate guides, suggestions, and adaptive feedback. We won’t dive into machine learning in this book, that will be a book in of itself. If you are interested, you can read Practical Machine Learning with Rust (www.apress.com/us/book/9781484251201). The microservice architecture in the backend allows you to create a variety of small services you can scale independently of each other but can also communicate as if they are on one endpoint (we will discuss how to do this when we get to Chapter 7). These microservices can then talk to database, bucket stores (like S3), or the message queue. All of that backend will process data, serve as endpoints to route data from mobile to the device, and even send notifications either to the device or the mobile application.
Why Rust?
The next question that may come to mind is why did we pick Rust? If you look at most web applications, they aren’t written in Rust; if you look at most board development, it isn’t in Rust either. So why Rust? Rust is a multi-paradigm programming language that focuses on performance and safety. Rust, by what it allows you to do, has quite a bit more performance and safety implemented than other languages. The biggest way this is shown is in Rust’s borrowing and ownership checks. Rust makes it so that there are specific rules around when a variable is borrowed, who owns it, and for how long they own it for. This has been the main attraction of Rust for many. The code becomes faster, less memory intensive, and less like to have two variables access each other at the same time. We will get into this more in the borrowing section. Stylistically, Rust is similar to languages like Go and has C-like syntax with pointers and references. And while some of the Rust crates lack the maturity of other languages, the language itself is continuously enhancing and added to.
Pre-requisites
- 1.
Learn Rust (www.rust-lang.org/learn) – Between reading the online book and the examples, you can gain quite a good understanding of book. The book is often updated and usually up to date. It’s how I initially learned Rust.
- 2.
Beginning Rust book (www.apress.com/gp/book/9781484234679) – Often it’s easier to learn through longer books that will go into greater detail, and if that’s what you need, Beginning Rust is for you.
In addition, throughout the book we are going to cover a number of topics from microservices, GraphQL, CQRS, Kubernetes, Docker, and more. And while I will provide some explanations and backgrounds for each technology we introduce, there are entire books devoted to each of those tools. If you ever feel you need to learn more, I would suggest looking online; we will give resource links during those chapters.
Components to Purchase
In the first half of this book, we will create a cloud application, and while we will be deploying that application to DigitalOcean cloud services, that isn’t actually a requirement in building everything. Even then, we are picking DigitalOcean over services like AWS to mitigate the cost.
- 1.
Raspberry Pi 4 4 GB Starter Kit (https://amzn.com/B07V5JTMV9) – This kit will run about $100 but will include everything we need to get the basic Raspberry Pi up and running: from cables to connect it to your monitor, to power cables, and even a 32 GB SD card to be used for application and video storage. There are cheaper kits you can buy, but the all-in-ones will be a great starting point. Note: I selected and used the 4 for development, but if you used a 3, it should work as well; you will just have to adjust some endpoints when downloading OS software. The full Pi 4 kit will cost roughly $100.
- 2.
Raspberry Pi Debug Cable (https://amzn.com/B00QT7LQ88) – This is a less than $10 cable that you can use to serially connect your Pi to your laptop without having to have a monitor, keyboard, or SSH ready. We will use this for initial setup, but if you are willing to hook your keyboard and monitor directly, it’s not necessary.
- 3.
Sense HAT (https://amzn.com/B014HDG74S) – The Sense HAT is an all-in-one unit that sits on all the Pi’s GPIO pins that provides an 8 x 8 LED matrix display as well as numerous accelerometer, gyroscope, pressure, humidity, temperature sensors, and a joystick. We will be making use of the temperature, LED, and joystick later in this book. But this HAT provides quite a bit for $35.
- 4.
Raspberry Pi Camera Module with 15 Pin Ribbon (https://amzn.com/B07JPLV5K1) – The camera we will be using is a $10 simple camera that is connected by ribbon to the Raspberry Pi. Since we are using this for simple video and face detection, the camera can be fairly basic, but it’s up to you how much you want to spend on it.
While I have given you Amazon links to purchase everything, you are free to purchase from any supplier; it’s all the same. This was just for ease of use.
We will cover and use all these components throughout the book.
Goals
The main goal of this book is to create a complete IoT application from the device all the way to the backend and all the parts in between. Without taking too many shortcuts, we will be using practices and techniques that are used for larger-scale applications. The goal is to give you all the lessons needed should you wish to expand your IoT application.
What we will actually be building is a HomeKit-enabled video recording device that stores and parses for metadata video and image files to the cloud and allow downloading and commenting on those videos. Here are the details:
Raspberry Pi – Allow a user to authenticate so that we know which Pi the files originate from. Allow the user to click a button on the Pi to see the temperature. Record video with facial recognition storing the video and image captures and sending the data to the cloud.
Cloud – Allow downloading and uploading of video and image files. Parse video and image files for metadata content. Create endpoints in the backend for users to create comments and query comments for the video files.
- 1.Server side
- a.
Creating a deployable set of microservices
- b.
Server application that exposes GraphQL endpoints
- c.
Server application that uploads and downloads files
- d.
Communicating with hardware securely via MQTT
- e.
Creating and using certificates
- f.
Creating Docker, Helm, and Kubernetes scripts to deploy the application
- a.
- 2.Hardware side
- a.
Setting up a Raspberry Pi
- b.
Adding peripherals to the development board/Raspberry Pi
- c.
Interacting with HomeKit
- d.
Capturing video data
- i.
Performing OpenCV on the video
- ii.
Recording and uploading video content
- iii.
Using SQLite to have a resilient store of data
- a.
Before we start coding, we are going to discuss the server and hardware side more.
Source Code
All of the source code for this book will be located on my GitHub page at http://github.com/nusairat/rustfortheiot.
This will include the services for the cloud, the applications for the Raspberry Pi, and the necessary build-and-deploy docker files. While I do step you through most of the code in the book, some of the more repetitive items like arguments for variables I only show you once to apply to your other services/applications. If you have any issues, please create an issue or you can tweet me at @nusairat. Now of course be aware that as the years go on, there could be compilation issues due to the version of Rust that is the current version. As of the time of finishing this book, the version of Rust is 1.43.1.
Web Application
- 1.Microservices
- a.
Upload/download service
- b.
Retrieval service
- c.
MQ service
- a.
- 2.
Postgres database
- 3.
Eventstore database
- 4.
Message queue
- 1.
Act as a remote storage. When recording video or images from your IoT device, we will store it locally on the IoT device initially. However, this is not ideal if we want to retrieve the data from a remote client device; the round trip would be extremely slow. In that case, the application would have to call a server, and the server would then have to call the IoT device and start the transfer of data. While this is fine for real-time live video, if you are trying to view lots of historical archives, the slow download speed would become uncomfortably noticeable. In addition, it’s great to have offsite storage of the data to serve as backup for your application. Most cloud providers will provide fairly cheap storage for large data; they just charge you for access to the data. As long as you aren’t constantly accessing the data, you are fine moneywise. Our cloud application will be able to store to the local file store of the server it’s on as well as to cloud storage services like S3. The reason for this will become apparent later, but this will allow us to run one of the upload services locally from a Raspberry Pi (or other server) co-located in your house and to the cloud. This can help lower costs for storage and servers and is common in any do-it-yourself system.
- 2.
Another issue we want to tackle is sending commands to the IoT device. Mobile devices allow you to send commands to your home units through the backend. In our application, we are going to allow recording start and stop commands to be sent via a RESTful endpoint to the backend that will control whether the Pi records or not.
- 3.
Querying of data. As you store more and more files, images, and video, you are going to want to add tags to these uploads but also search for them, not only custom tags but the metadata associated with the files. Images and video often have metadata created and stored with them. These can include things like location, time, aperture and other settings (for video/camera), quality rate, and so on. These are all services the user will want to search for. We will parse the video and image files and store their metadata for use later.
Board Application
When we first started thinking of what we wanted to use with Rust, the board is what attracted us the most. With the board, there are many options from your Raspberry Pi to a more advanced iMX.8 board, which we initially thought of going for, but then the Raspberry Pi 4 came out. The 4 is an extremely powerful and advanced board, and it is not only a hobbyist board of choice but is often used in the real world. In the real world, before you’ve created your custom chipset, design, and others for your hardware piece, the Pi can serve a short-term prototyping tool that your engineers can work on while waiting for revisions of the production board. Raspberry Pis are the hobbyist choice because of their cost, size, and ability. There are a few ways of creating IoT solutions, and companies employ a variety of solutions. You have anywhere that range from relatively dumb devices that do one thing like take record temperature and send it back to a common device (think of Ecobee’s thermostat sensors) to a more encompassing device that has speakers and cameras like a a smart doorbell, or even more advance that has monitors, sensors, and so on. With the Pi, our options are a bit more limitless since we can attach whatever sensors we want to the Pi. In addition for something that is just recording temperature, you could go down to a cheaper Pi Zero. All of this will be future options for your components; for now, we are sticking with one Pi that has all the components attached to itself.
With this setup, we are going to be able to record video via attached camera, have display interactions, and show the temperature. The purpose is to give you a powerful starting point for creating your own applications. You will interact with the GPIO and the camera port and learn how to build and deploy applications to the board. One of the biggest hurdles will be how to run multiple processes at once in Rust on the board to perform heartbeats, face monitoring, and receive input.
- 1.Face recognition video recording
- a.
Background uploading of video
- a.
- 2.Communicate with MQTT
- a.
Send heartbeat
- b.
Receive recording commands
- a.
- 3.LED display
- a.
Display pictures for holidays
- b.
Display device code for authentication
- c.
Display temperature
- a.
- 4.Homekit integration
- a.
Display temperature
- b.
Display motion detection
- a.
Basic Rust
While I mentioned a few other resources for learning Rust, I feel I’d be remiss if I did not at least cover a basic introduction and touched on topics and language syntax that you will see throughout the book. As software developers, and especially modern-day software developers, switching languages is part of our everyday job; as a community, we keep evolving to solve new problems. In this final section of Chapter 1, we are going to discuss the Rust language, its syntax, and its features and go over some code samples which will help you understand the language. If you already are comfortable with Rust, you can skip this section and start Chapter 2; if not, read on.
Rust Origins
Rust is not a new language but rather has been around since 2008 but until recently got popular in the main stream. It was started by and still the biggest contributors to it are Mozilla. It was mainly used as a language for the Mozilla browser engine. Rust syntactically is like C/C++ with the standard curly brackets and language syntax. However, it is not the class-based inheritance-type language you find with C/C++ or Java; instead, it’s a very functional language. The main focus of rust was speed and memory safety. And it exceeds in both; it is extremely fast beating, even Golang in many tests as well as C and C++. Memory management like we discussed earlier is very competent without you having to micromanage the application. So what are the places to use Rust? Well, here are four significant areas for Rust.
Command Line
Rust has great support for command-line tools and can run quickly and start up fast. There are tools to make it easily distributable as a binary to others (we will be creating these later). You can also create a robust set of configurations to make it work in multiple environments, have first world logging, and can talk to about any set of data points. The final item, the data points, makes it very good as a cloud-based middleman service. If you need a service that monitors webhooks, databases, and message queues, it can serve in a low CPU/memory pod funneling and processing data between other systems all the while not exposing NodePorts or easily attack vectors into the pod.
Modules
For existing or even for more full-featured applications, there are often slow components or performance critical components for it. Rust can be used in many languages to perform high-throughput processing of data, JSON, and so on. WebAssembly is a commonly used for this to create performance critical JavaScript and wrapped in a module.
Microservices
While not traditionally designed for web applications, microservice architecture is a great use of Rust. This often fills the same space that Golang and other languages try to create. Rust’s speed and memory management lends it well to microservices that are accessed repeatedly but where you may fear memory growing out of control. Much like Golang, Rust can use Docker scratch containers (more on this later) that will allow you to deploy an application with low memory footprint. This, for anyone deploying to the cloud, will save money in the long run; this is one of the biggest reasons shops are switching from the traditional JVM model to the scratch container model.
Embedded
Embedded is where the biggest need for a next-generation language really relies. For decades now, C and C++ have been the language of choice for embedded designs. Because of this, there are already many libraries written in C for working with embedded systems. To help this, there is some shared interoperability between the languages, including Rust containing C-type data structures that can be used in your code. We will go over these later in the chapter. The two biggest reasons is that it’s compiled down, so it can be run without an interpreter, and its memory-safe by writing your own memory allocation and deallocation. However, Rust really helps to solve both problems; it’s compiled to byte code, and the memory management has rules that it forces you to live by making the memory allocation predictable and repeatable. Rust also makes it impossible to perform accidental concurrency.
So of those four areas, we will cover in this book the command line, microservices, and embedded.
Learning Rust
This book at its heart is not a “learning rust” book; it is more a book on systems engineering of an IoT system with Rust. You will need to know at least some Rust to understand the examples. If you haven’t learned it yet, there are many great resources to learn the Rust language. Probably the easiest and best is the Rust Programming Language written by Nicholas Matsakis and Aaron Turon; it’s an online book you can download and use (https://doc.rust-lang.org/book/index.html). Or if you want more details, Carlo Milanesi has Beginning Rust: From Novice to Professional that is also useful. I’ve also read the Rust in Action MEAP, and that book goes into some very deep concepts and base coding that can be fascinating as well, but I don’t think it’s necessarily a book for beginners.
Now this being said, if you are like us, picked up the book and wanted to dive in as fast as you can, then this next part is for you. We will go over many of the basic concepts of Rust and the syntax and rules for the language including many of the features we are going to use in the book. This section is obviously much shorter than a book or a site dedicated but should help you at least get your feet wet enough to be able to read many of these examples of the book. This is far from comprehensive but should get you going enough to be dangerous.
Let us dive in with what the most basic Rust application looks like, and we can build up the application from there. So let’s start with the traditional Hello World application.
Installing Rust
First up in order to use Rust, you will have to install rust. While this seems straightforward , my experience has been it is not as straightforward as it usually is. Often when I am installing a new application (and note I am on a Mac), I just go to Homebrew and install the new tool set, be it Clojure, Python, Golang, or Elixir. With Rust, you don’t necessarily want to go that route.
Do not use Homebrew to install Rust directly.
What you want to install is rustup; this is the system installer for Rust. Part of the reason for this is you do not want to install just Rust, you are going to want to install a few other items that will make your life easier:
Cargo – This is the package manager for Rust we will be using throughout the book and our examples to run Rust applications.
RLS (Rust Language Server) – While you can run Rust from the command line without RLS, you will want RLS if you plan to use VS Code and other editors. RLS will allow rust to compile and run from your editors.
Installing rustup
Now that you have Rust installed, we can move on and start coding in Rust.
Hello World
main.rs, the most basic application
Commands to compile and run the application
OK, so now that we have the customary introduction to every language, let’s start diving into doing things more than println.
Writing in Rust
A theme you will see in this book and in general talking about Rust is that it has C/C++-like syntax and the memory management, so consequently those are the two major things we will be discussing and showing. Much of this chapter is showing you the syntax and the features; for those of you who want to dive all in to Rust, let’s get to it.
Variables and Data Types
Setting values three different ways
- 1.
The first line is setting a to the number 3; by default, this will be set to a medium size number type of u32 (more about that in a bit).
- 2.
The second line should be familiar to most; the single line denotes setting a char.
- 3.
Finally, the third line is setting what looks to be a String, like we have in most languages. This however is not; it is what’s called a string slice. And in fact, it has a static lifetime, which when you think about, makes sense since we have hard-coded the string on the right; it isn’t a variable that will get changed.
It will be important to understand the difference between string slices and Strings and when to use each.
Setting values three different ways with types
Type | Signed | Unsigned | C++ Equivalent | Java Equivalent |
---|---|---|---|---|
8-bit | i8 | u8 | int8_t/unit8_t | byte |
16-bit | i16 | u16 | short int | short |
32-bit | i32 | u32 | int/float | int |
64-bit | i64 | u64 | double/long double | long/double |
128-bit | i128 | u128 | custom | BigInteger |
arch | N/A | N/A | isize | usize |
Note: The 8-bit one in C++ is an unsigned char.
Changing the Variable
Mutation example
① Changing a value of a variable you have set.
② Changing a value of a variable you have not set; in this case, you HAVE to set the type.
③ Changing a value of a variable whose type has also been set.
Changing Value That You Have Passed In
There are also other types you can define and use. In Listing 1-7 we have examples of those types.
Type | Sample |
---|---|
bool | true/false |
char | 'a'/'1F63B','U+10FFFF' |
array | |
slice | |
str | |
tuple | (i32, f64) |
Example of using a variety of types
Borrowing and Ownership
We mentioned the borrowing and ownership earlier, and this is one of the keys to the Rust language. And honestly, this will be the hardest part to wrap your head around when coming from other languages that have very loose borrowing rules. When you first start writing Rust code, you will probably get quite a bit of borrow checker errors on compilation; this is normal, and the more advance of a feature, the better it will be. I still get the same problems, albeit it’s usually due to the library I am using.
Setting a variable and then changing the contents to another variable
Setting a variable and then changing the contents to another variable but with a struct
This is basically doing the same thing as the previous, but this one will fail with a “value borrowed here after move” for the println. Why is this? Let’s break down what’s happening. On the first line of the method, n is initialing the Number to a section of memory. On the second line, that memory is now pointed to mv instead of n. Since only one section of memory has a variable pointed to it, n now has nothing pointed. This makes sense but begs the question why did the first set of code work, but this code fail. That is because in the first set it was actually performing a copy of the memory instead. Thus, zed and me were both pointing to different memory both holding “4”. We can actually do the same with the second example by adding Copy and Clone to the derive; this will allow the compiler to automatically have in the second reference to perform a copy task instead. You will see that the borrowing and reference are all done via compiler optimization as well, making it much faster.
Passing a u32 to a method
Passing a String to a method
Passing ownership to a method and then returning it
Note you do have to make the variable mutable to set it; if not, you would have received a “cannot assign twice to immutable variable” had you tried to reassign x.
Memory
When talking about borrowing, it’s also useful to talk about the lifetime of the memory associated with it. Memory in any system is finite, and if you’ve ever worked with a large or heavy use application, you’ve probably run into some memory issues. Much of this is simply because the way other systems handle memory isn’t optimal. When we talk about C, we would use alloc and dealloc to allocate and deallocate the memory accordingly. This is a manual process, but gives the developer great flexibility in controlling memory usage. However, I’ve seen quite a few times this also leads to memory leaks due to programmer error. Java decided to take this out of the programmer’s hand and used garbage collection to clean up memory. Periodically, Java will run a garbage collector to free up what it considers memory no longer in use. This works great most of the time; however, this gives the developer no ability to control memory cleanup and in heavy use or an application creating quite a bit of objects no ability to control its cleanup.
Implementing the Drop trait for Person
① Implement Drop trait on Person.
② Instantiate a Person and create the memory for it.
③ At this point, the variable will go out of memory.
④ Destructor is called.
As you can see, the destructor gets called once we return from the function. Since most of Rust’s memory is by default allocated to the stack as well, the memory space is now free to be used by any other item initialized down the chain.
Reference and Dereferencing
Passing the variable as a reference and dereferencing it to get set
Traits
Most of the Rust we have discussed are fairly common features between languages (most languages have concepts of methods or functions and variables). Traits are very different and are how we create abstraction in Rust. Traits are abstraction layers that work on structs to add functionality to yours or to existing structs within the framework. We will see in the chapters to come they are used extensively to create functionality for existing middleware.
A Person struct and its optional implementation
A student trait
Implement student on the person
① The syntax to say the implementation of trait Student for Person.
② A new to construct the trait. This is optional.
③ Finally, one of the methods used.
Instantiate and use the trait
Using a trait without instantiating it on a struct
① Adding this line in your module allowed the compiler now is required in this case.
The addition of the Student along with the Person allows the compiler to know the Person will have Student functions as well; this is what allows us to add traits to structs that we didn’t create in our application.
Typemap
Typemap example
We will use this later with the iron framework to add middleware data to Requests. In addition, there are a few other features using Arc<Mutex<> and the async/await pattern that we will dive into in later chapters when we actually need to use them.
Cargo
Compiling and running code is obviously a must have for any code written, and while we can always compile and run applications from the command line with the language compiler, eventually (usually early on) a package manager (or 8 if you are Java) comes out. Rust is no exception to that of course, and hence we have Cargo. Cargo is the default package manager used by Rust and for Rust projects and in all the coming chapters is what we are going to be using to run our examples and applications. Luckily, Cargo is distributed with by default so we do not have to take any extra steps to get it working.
cargo run – Runs the binary or the source application
cargo test – Runs all the tests in the application
cargo doc – Creates the documentation from the application using the commenting
cargo bench – Benchmarks the package
cargo build – Builds the application into an executable
The two commands you will use the most are run and test. The build you will normally run when performing a release, so that will occur when you run cargo build --release. There are also third-party extensions to add more functionality to your cargo releases. You can find a various amount here: https://github.com/rust-lang/cargo/wiki/Third-party-cargo-subcommands.
Feature Flags
Feature flags are a way of activating code at compile time or runtime based on a given feature. We will use this when importing crates in the application. With feature flags, you may want to activate certain set of code vs. other sets. For example, inside the ORM we will be using for the database, you could use different versions of the UUID crate. This is quite common in Rust. Feature flags are first defined in your Cargo.toml file and then activated at compile time in your code by encapsulating the method, block, and module with #[cfg(feature = "feature-name")] that would then activate that set of code when the feature-name is supplied. I wanted to mention them because while I won’t actively be using them in the chapters, I will be using them in the source code. Most of our code builds up on itself as we go along either by adding new services or new modules. However, there are many instances that as we progress from chapter to chapter, we have to change a method signature or drastically update. In these instances, I have added feature flags to the code that accompanies this book. The flag full will always be for the final book, but often I use ch05 to compile the code for that specific chapter. The README.md for each module will tell you which flags are supported for that application; of course, you can also look at the Cargo.toml to find out as well.
Creating a Release
- 1.
It first will check that the current working directory is git clean; now this usually won’t affect you since releases should be performed on a build system, but it’s useful to know you do not have any outstanding commits before creating a release.
- 2.
It bumps the release level if set. This one is a bit more complicated, in that there are different rules based on the level you are releasing, and has to do with the version you set in your Cargo.toml. If you had marked it as pre-release (i.e., 0.1.0-pre), it will simply remove the pre. If the level is a patch and there is no pre-release associated with it, then it will bump the minor version. If the level is minor, it will bump the minor (i.e., 0.1.0-pre to 0.2.0), and if the level is major, it will bump the major version (0.1.0 to 1.0.0). You can also use alpha, beta, and rc for levels as well. We will use this for our application on the hardware being released, but won’t be using it for.
- 3.
Run cargo publish if you want to publish this project; this is useful for crates you are creating for public consumption.
- 4.
It generates the rustdoc and pushes to gh-pages (optionally); again this is good for a projection that is for a public crate hosted on github.
- 5.
It generates a git tag with the number of the version; this is required so that you can easily see the source code associated with a given release.
- 6.
In the main, it will then bump the version for the next development cycle.
- 7.
Finally, it pushes these changes, the bump and the tag, to git.
Summary
In the introduction, we presented the requirements and goals of the book and described the applications we will be creating for it. We also covered some topics related to web and board applications as well as ran through some basic Rust language tutorials. In the next chapter, we will start coding our first microservice.