Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Rust Web Development with Rocket
Rust Web Development with Rocket

Rust Web Development with Rocket: A practical guide to starting your journey in Rust web development using the Rocket framework

eBook
€12.99 €18.99
Paperback
€23.99
Subscription
Free Trial
Renews at €18.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Table of content icon View table of contents Preview book icon Preview Book

Rust Web Development with Rocket

Chapter 1: Introducing the Rust Language

Almost every programmer has heard about the Rust programming language or even tried or used it. Saying "the Rust programming language" every time is a little bit cumbersome, so let's just call it Rust, or the Rust language from this point forward.

In this chapter, we will talk a little bit about Rust to help you if you are new to this language or as a refresher if you have tried it already. This chapter might also help seasoned Rust language programmers a bit. Later in the chapter, we will learn how to install the Rust toolchain and create a simple program to introduce the features of the Rust language. We will then use third-party libraries to enhance one of our programs, and finally, we will see how we can get help for the Rust language and its libraries.

In this chapter, we're going to cover the following main topics:

  • An overview of the Rust language
  • Installing the Rust compiler and toolchain
  • Writing Hello World
  • Exploring Rust crates and Cargo
  • Exploring other tools and where to get help

Technical requirements

To follow the content of this book, you will need a computer running a Unix-like operating system such as Linux, macOS, or Windows with Windows Subsystem for Linux (WSLv1 or WSLv2) installed. Don't worry about the Rust compiler and toolchain; we will install it in this chapter if you don't have it installed already.

The code for this chapter can be found at https://github.com/PacktPublishing/Rust-Web-Development-with-Rocket/tree/main/Chapter01.

An overview of the Rust language

To build web applications using the Rocket framework, we must first learn a bit about the Rust language since Rocket is built using that language. According to https://www.rust-lang.org, the Rust language is "a language empowering everyone to build reliable and efficient software." It began as a personal project for a programmer named Graydon Hoare, an employee at Mozilla, around 2006. The Mozilla Foundation saw the potential of the language for their product; they started sponsoring the project in 2009 before announcing it to the public in 2010.

Since its inception, the focus of Rust has always been on performance and safety. Building a web browser is not an easy job; an unsafe language can have very fast performance, but programmers working with system languages without adequate safety measures in place can make a lot of mistakes, such as missing pointer references. Rust was designed as a system language and learned many mistakes from older languages. In older languages, you can easily shoot yourself in the foot with a null pointer, and nothing in the language prevents you from compiling such mistakes. In contrast, in the Rust language, you cannot write a code that resulted in null pointer because it will be detected during compile time, and you must fix the implementation to make it compile.

A lot of the Rust language design is borrowed from the functional programming paradigm, as well as from the object-oriented programming paradigm. For example, it has elements of a functional language such as closures and iterators. You can easily make a pure function and use the function as a parameter in another function; there are syntaxes to easily make closures and data types such as Option or Result.

On the other hand, there are no class definitions, but you can easily define a data type, for example, a struct. After defining that data type, you can create a block to implement its methods.

Even though there is no inheritance, you can easily group objects by using traits. For example, you can create a behavior and name it the MakeSound trait. Then, you can determine what methods should be in that trait by writing the method signatures. If you define a data type, for example, a struct named Cow, you can tell the compiler that it implements a MakeSound trait. Because you say the Cow struct implements the MakeSound trait, you have to implement the methods defined in the trait for the Cow struct. Sounds like an object-oriented language, right?

The Rust language went through several iterations before a stable version was released (Rust 1.0) on May 15, 2015. Some of the early language design was scrapped before releasing the stable release. At one point, Rust had a class feature but this was scrapped before the stable release because Rust design was changed to have data and behavior separation. You write data (for example, in the form of a struct or enum type), and then you write a behavior (for example, impl) separately. To categorize those impl in the same group, we can make a trait. So, all the functionality you would want from an object-oriented language can be had thanks to that design. Also, Rust used to have garbage collection, but it was then scrapped because another design pattern was used. When objects get out of scope, such as exiting a function, they are deallocated automatically. This type of automatic memory management made garbage collection unnecessary.

After the first stable release, people added more functionalities to make Rust more ergonomic and usable. One of the biggest changes was async/await, which was released in version 1.39. This feature is very useful for developing applications that handle I/O, and web application programming handles a lot of I/O. Web applications have to handle database and network connections, reading from files, and so on. People agree that async/await was one of the most needed features to make the language suitable for web programming, because in async/await, the program doesn't need to make a new thread, but it's also not blocking like a conventional function.

Another important feature is const fn, a function that will be evaluated at compile-time instead of runtime.

In recent years, many large companies have started to build a talent pool of Rust developers, which highlights its significance in business.

Why use the Rust language?

So, why should we use the Rust language for web application development? Aren't existing established languages good enough for web development? Here are a few reasons why people would want to use the Rust language for creating web applications:

  • Safety
  • No garbage collection
  • Speed
  • Multithreading and asynchronous programming
  • Statically typed

Safety

Although writing applications using a system programming language is advantageous because it's powerful (a programmer can access the fundamental building block of a program such as allocating computer memory to store important data and then deallocating that memory as soon as it is not in use), it's very easy to make mistakes.

There's nothing in a traditional system language to prevent a program from storing data in memory, creating a pointer to that data, deallocating the data stored in memory, and trying to access the data again through that pointer. The data is already gone but the pointer is still pointing to that part of the memory.

Seasoned programmers might easily spot such mistakes in a simple program. Some companies force their programmers to use a static analysis tool to check the code for such mistakes. But, as programming techniques become more sophisticated, the complexity of the application grows, and these kinds of bugs can still be found in many applications. High-profile bugs and hacks found in recent years, such as Heartbleed, can be prevented if we use a memory-safe language.

Rust is a memory-safe language because it has certain rules regarding how a programmer can write their code. For example, when the code is compiled, it checks the lifetime of a variable, and the compiler will show an error if another variable still tries to access the already out-of-scope data. Ralf Jung, a postdoctoral researcher, already made the first formal verification in 2020 that the Rust language is indeed a safe language. Built-in data types, such as Option or Result, handle null-like behavior in a safe manner.

No garbage collection

Many programmers create and use different techniques for memory management due to safety problems. One of these techniques is garbage collection. The idea is simple: memory management is done automatically during runtime so that a programmer doesn't have to think about memory management. A programmer just needs to create a variable, and when the variable is not used anymore, the runtime will automatically remove it from memory.

Garbage collection is an interesting and important part of computing. There are many techniques such as reference counting and tracing. Java, for example, even has several third-party garbage collectors besides the official garbage collector.

The problem with this language design choice is that garbage collection usually takes significant computing resources. For example, a part of the memory is still not usable for a while because the garbage collector has not recycled that memory yet. Or, even worse, the garbage collector is not able to remove used memory from the heap, so it will accumulate, and most of the computer memory will become unusable, or what we usually call a memory leak. In the stop-the-world garbage collection mechanism, the whole program execution is paused to allow the garbage collector to recycle the memory, after which the program execution is resumed. As such, some people find it hard to develop real-time applications with this kind of language.

Rust takes a different approach called resource acquisition is initialization (RAII), which means an object is deallocated automatically as soon as it's out of scope. For example, if you write a function, an object created in the function will be deallocated as soon as the function exits. But obviously, this makes Rust very different compared to programming languages that deallocate memory manually or programming languages with garbage collection.

Speed

If you are used to doing web development with an interpreted language or a language with garbage collection, you might say that we don't need to worry about computing performance as web development is I/O bound; in other words, the bottleneck is when the application accesses the database, disk, or another network, as they are slower than a CPU or memory.

The adage might be primarily true but it all depends on application usage. If your application processes a lot of JSON, the processing is CPU-bound, which means it is limited by the speed of your CPU and not the speed of disk access or the speed of network connection. If you care about the security of your application, you might need to work with hashing and encryption, which are CPU-bound. If you are writing a backend application for an online streaming service, you want the application to work as optimally as possible. If you are writing an application serving millions of users, you want the application to be very optimized and return the response as fast as possible.

The Rust language is a compiled language, so the compiler will convert the program into machine code, which a computer processor can execute. A compiled language usually runs faster than an interpreted language because, in an interpreted language, there is an overhead when the runtime binary interprets the program into native machine code. In modern interpreters, the speed gap is reduced by using modern techniques such as a just-in-time (JIT) compiler to speed up the program execution, but in dynamic languages such as Ruby, it's still slower than using a compiled language.

Multithreading and asynchronous programming

In traditional programming, synchronous programming means the application has to wait until CPU has processed a task. In a web application, the server waits until an HTTP request is processed and responded to; only then does it go on to handle another HTTP request. This is not a problem if the application just directly creates responses such as simple text. It becomes a problem when the web application has to take some time to do the processing; it has to wait for the database server to respond, it has to wait until the file is fully written on the server, and it has to wait until the API call to the third-party API service is done successfully.

One way to overcome the problem of waiting is multithreading. A single process can create multiple threads that share some resources. The Rust language has been designed to make it easy to create safe multithreaded applications. It's designed with multiple containers such as Arc to make it easy to pass data between threads.

The problem with multithreading is that spawning a thread means allocating significant CPU, memory, and OS resources, or what is colloquially known as being expensive. The solution is to use a different technique called asynchronous programming, where a single thread is reused by different tasks without waiting for the first task to finish. People can easily write an async program in Rust because it's been incorporated into the language since November 7, 2019.

Statically-typed

In programming languages, a dynamically-typed language is one where a variable type is checked at runtime, while a statically-typed language checks the data type at compile time.

Dynamic typing means it's easier to write code, but it's also easier to make mistakes. Usually, a programmer has to write more unit tests in dynamically-typed languages to compensate for not checking the type at compile time. A dynamically-typed language is also considered more expensive because every time a function is called, the routine has to check the passed parameters. As a result, it's difficult to optimize a dynamically-typed language.

Rust, on the other hand, is statically-typed, so it's very hard to make mistakes such as passing a string as a number. The compiler can optimize the resulting machine code and reduce programming bugs significantly before the application is released.

Now that we have provided an overview of the Rust language and its strengths compared to other languages, let's learn how to install the Rust compiler toolchain, which will be used to compile Rust programs. We'll be using this toolchain throughout this book.

Installing the Rust compiler toolchain

Let's start by installing the Rust compiler toolchain. Rust has three official channels: stable, beta, and nightly. The Rust language uses Git as its version control system. People add new features and bug fixes to the master branch. Every night, the source code from the master branch is compiled and released to the nightly channel. After six weeks, the code will be branched off to the beta branch, compiled, and released to the beta channel. People will then run various tests in the beta release, most often in their CI (Continuous Integration) installation. If a bug is found, the fix will be committed to the master branch and then backported to the beta branch. Six weeks after the first beta branch-off, the stable release will be created from the beta branch.

We will use the compiler from the stable channel throughout the book, but if you feel adventurous, you can use the other channels as well. There's no guarantee the program we're going to create will compile if you use another channel though because people add new features and there might be regression introduced in the new version.

There are several ways to install the Rust toolchain in your system, such as bootstrapping and compiling it from scratch or using your OS package manager. But, the recommended way to install the Rust toolchain in your system is by using rustup.

The definition on its website (https://rustup.rs) is very simple: "rustup is an installer for the systems programming language Rust." Now, let's try following these instructions to install rustup.

Installing rustup on the Linux OS or macOS

These instructions apply if you are using a Debian 10 Linux distribution, but if you are already using another Linux distribution, we're going to assume you are already proficient with the Linux OS and can adapt these instructions suitable to your Linux distribution:

  1. Open your terminal of choice.
  2. Make sure you have cURL installed by typing this command:
    curl
  3. If cURL is not installed, let's install it:
    apt install curl

If you are using macOS, you will most likely already have cURL installed.

  1. After that, follow the instructions on https://rustup.rs:
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  2. It will then show a greeting and information, which you can customize; for now, we're just going to use the default setup:
    ...
    1) Proceed with installation (default)
    2) Customize installation
    3) Cancel installation
    >
  3. Type 1 to use the default installation.
  4. After that, reload your terminal or type this in the current terminal:
    source $HOME/.cargo/env
  5. You can confirm whether the installation was successful or not by typing rustup in the Terminal and you should see the usage instruction for rustup.
  6. Now, let's install the stable Rust toolchain. Type the following in the terminal:
    rustup toolchain install stable
  7. After the toolchain has been installed into your OS, let's confirm whether we can run the Rust compiler. Type rustc in the terminal and you should see the instructions on how to use it.

Installing a different toolchain and components

Right now, we have the stable toolchain installed, but there are two other default channels that we can install: nightly and beta.

Sometimes, you might want to use a different toolchain for various reasons. Maybe you want to try a new feature, or maybe you want to test regression in your application against an upcoming version of Rust. You can simply install it by using rustup:

rustup toolchain install nightly

Each toolchain has components, some of which are required by the toolchain, such as rustc, which is the Rust compiler. Other components are not installed by default, for example, clippy, which provides more checks not provided by the rustc compiler and gives code style suggestions as well. To install it is also very easy; you can use rustup component add <component> as shown in this example:

rustup default stable
rustup component add clippy

Updating the toolchain, rustup, and components

The Rust toolchain has a regular release schedule of around every three months (six weeks plus six weeks), but sometimes there's an emergency release for a major bug fix or a fix for a security problem. As a result, you sometimes need to update your toolchain. Updating is very easy. This command will also update the components installed in the toolchain:

rustup update

Besides the toolchain, rustup itself might also be updated. You can update it by typing the following:

rustup self update

Now that we have the Rust compiler toolchain installed in our system, let's write our first Rust program!

Writing Hello World!

In this section, we are going to write a very basic program, Hello World!. After we successfully compile that, we are going to write a more complex program to see the basic capabilities of the Rust language. Let's do it by following these instructions:

  1. Let's create a new folder, for example, 01HelloWorld.
  2. Create a new file inside the folder and give it the name main.rs.
  3. Let's write our first code in Rust:
    fn main() { 
        println!("Hello World!");
    }
  4. After that, save your file, and in the same folder, open your terminal, and compile the code using the rustc command:
    rustc main.rs
  5. You can see there's a file inside the folder called main; run that file from your terminal:
    ./main
  6. Congratulations! You just wrote your first Hello World program in the Rust language.

Next, we're going to step up our Rust language game; we will showcase basic Rust applications with control flow, modules, and other functionalities.

Writing a more complex program

Of course, after making the Hello World program, we should try to write a more complex program to see what we can do with the language. We want to make a program that captures what the user inputted, encrypts it with the selected algorithm, and returns the output to the terminal:

  1. Let's make a new folder, for example, 02ComplexProgram. After that, create the main.rs file again and add the main function again:
    fn main() {}
  2. Then, use the std::io module and write the part of the program to tell the user to input the string they want to encrypt:
    use std::io;
    fn main() {
        println!("Input the string you want to encrypt:");
        let mut user_input = String::new();
        io::stdin()
            .read_line(&mut user_input)
            .expect("Cannot read input");
        println!("Your encrypted string: {}", user_input);
    }

Let's explore what we have written line by line:

  1. The first line, use std::io;, is telling our program that we are going to use the std::io module in our program. std should be included by default on a program unless we specifically say not to use it.
  2. The let... line is a variable declaration. When we define a variable in Rust, the variable is immutable by default, so we must add the mut keyword to make it mutable. user_input is the variable name, and the right hand of this statement is initializing a new empty String instance. Notice how we initialize the variable directly. Rust allows the separation of declaration and initialization, but that form is not idiomatic, as a programmer might try to use an uninitialized variable and Rust disallows the use of uninitialized variables. As a result, the code will not compile.
  3. The next piece of code, that is, the stdin() function, initializes the std::io::Stdin struct. It reads the input from the terminal and puts it in the user_input variable. Notice that the signature for read_line() accepts &mut String. We have to explicitly tell the compiler we are passing a mutable reference because of the Rust borrow checker, which we will discuss later in Chapter 9, Displaying User's Post. The read_line() output is std::result::Result, an enum with two variants, Ok(T) and Err(E). One of the Result methods is expect(), which returns a generic type T, or if it's an Err variant, then it will cause panic with a generic error E combined with the passed message.
  4. Two Rust enums (std::result::Result and std::option::Option) are very ubiquitous and important in the Rust language, so by default, we can use them in the program without specifying use.

Next, we want to be able to encrypt the input, but right now, we don't know what encryption we want to use. The first thing we want to do is make a trait, a particular code in the Rust language that tells the compiler what functionality a type can have:

  1. There are two ways to create a module: create module_name.rs or create a folder with module_name and add a mod.rs file inside that folder. Let's create a folder named encryptor and create a new file named mod.rs. Since we want to add a type and implementation later, let's use the second way. Let's write this in mod.rs:
    pub trait Encryptable {
        fn encrypt(&self) -> String;
    }
  2. By default, a type or trait is private, but we want to use it in main.rs and implement the encryptor on a different file, so we should denote the trait as public by adding the pub keyword.
  3. That trait has one function, encrypt(), which has self-reference as a parameter and returns String.
  4. Now, we should define this new module in main.rs. Put this line before the fn main block:
    pub mod encryptor;
  5. Then, let's make a simple type that implements the Encryptable trait. Remember the Caesar cipher, where the cipher substitutes a letter with another letter? Let's implement the simplest one called ROT13, where it converts 'a' to 'n' and 'n' to 'a', 'b' to 'o' and 'o' to 'b', and so on. Write the following in the mod.rs file:
    pub mod rot13;
  6. Let's make another file named rot13.rs inside the encryptor folder.
  7. We want to define a simple struct that only has one piece of data, a string, and tell the compiler that the struct is implementing the Encryptable trait. Put this code inside the rot13.rs file:
    pub struct Rot13(pub String);
    impl super::Encryptable for Rot13 {}

You might notice we put pub in everything from the module declaration, to the trait declaration, struct declaration, and field declaration.

  1. Next, let's try compiling our program:
    > rustc main.rs 
    error[E0046]: not all trait items implemented, missing: `encrypt`
     --> encryptor/rot13.rs:3:1
      |
    3 | impl super::Encryptable for Rot13 {}
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing 
      `encrypt` in implementation
      | 
     ::: encryptor/mod.rs:6:5
      |
    6 |     fn encrypt(&self) -> String;
      |     ----------------------------------------------
      ------ `encrypt` from trait
    error: aborting due to previous error
    For more information about this error, try `rustc --explain E0046`.

What is going on here? Clearly, the compiler found an error in our code. One of Rust's strengths is helpful compiler messages. You can see the line where the error occurs, the reason why our code is wrong, and sometimes, it even suggests the fix for our code. We know that we have to implement the super::Encryptable trait for the Rot13 type.

If you want to see more information, run the command shown in the preceding error, rustc --explain E0046, and the compiler will show more information about that particular error.

  1. We now can continue implementing our Rot13 encryption. First, let's put the signature from the trait into our implementation:
    impl super::Encryptable for Rot13 {
        fn encrypt(&self) -> String {
        }
    }

The strategy for this encryption is to iterate each character in the string and add 13 to the char value if it has a character before 'n' or 'N', and remove 13 if it has 'n' or 'N' or characters after it. The Rust language handles Unicode strings by default, so the program should have a restriction to operate only on the Latin alphabet.

  1. On our first iteration, we want to allocate a new string, get the original String length, start from the zeroeth index, apply a transformation, push to a new string, and repeat until the end:
    fn encrypt(&self) -> String {
        let mut new_string = String::new();
        let len = self.0.len();
        for i in 0..len {
            if (self.0[i] >= 'a' && self.0[i] < 'n') || 
            (self.0[i] >= 'A' && self.0[i] < 'N') {
                new_string.push((self.0[i] as u8 + 13) as 
                char);
            } else if (self.0[i] >= 'n' && self.0[i] < 
            'z') || (self.0[i] >= 'N' && self.0[i] < 'Z') 
            {
                new_string.push((self.0[i] as u8 - 13) as 
                char);
            } else {
                new_string.push(self.0[i]);
            }
        } 
        new_string
    }
  2. Let's try compiling that program. You will quickly find it is not working, with all errors being `String` cannot be indexed by `usize`. Remember that Rust handles Unicode by default? Indexing a string will create all sorts of complications, as Unicode characters have different sizes: some are 1 byte but others can be 2, 3, or 4 bytes. With regard to index, what exactly are we saying? Is index means the byte position in a String, grapheme, or Unicode scalar values?

In the Rust language, we have primitive types such as u8, char, fn, str, and many more. In addition to those primitive types, Rust also defines a lot of modules in the standard library, such as string, io, os, fmt, and thread. These modules contain many building blocks for programming. For example, the std::string::String struct deals with String. Important programming concepts such as comparison and iteration are also defined in these modules, for example, std::cmp::Eq to compare an instance of a type with another instance. The Rust language also has std::iter::Iterator to make a type iterable. Fortunately, for String, we already have a method to do iteration.

  1. Let's modify our code a little bit:
    fn encrypt(&self) -> String {
        let mut new_string = String::new();
        for ch in self.0.chars() {
            if (ch >= 'a' && ch < 'n') || (ch >= 'A' &&
            ch < 'N') {
                new_string.push((ch as u8 + 13) as char);
            } else if (ch >= 'n' && ch < 'z') || (ch >= 
            'N' && ch < 'Z') {
                new_string.push((ch as u8 - 13) as char);
            } else {
                new_string.push(ch);
            }
        }
        new_string
    }
  2. There are two ways of returning; the first one is using the return keyword such as return new_string;, or we can write just the variable without a semicolon in the last line of a function. You will see that it's more common to use the second form.
  3. The preceding code works just fine, but we can make it more idiomatic. First, let's process the iterator without the for loop. Let's remove the new string initialization and use the map() method instead. Any type implementing std::iter::Iterator will have a map() method that accepts a closure as the parameter and returns std::iter::Map. We can then use the collect() method to collect the result of the closure into its own String:
    fn encrypt(&self) -> Result<String, Box<dyn Error>> {
        self.0
            .chars()
            .map(|ch| {
                if (ch >= 'a' && ch < 'n') || (ch >= 'A' 
                && ch < 'N') {
                    (ch as u8 + 13) as char
                } else if (ch >= 'n' && ch < 'z') || (
                ch >= 'N' && ch < 'Z') {
                    (ch as u8 - 13) as char
                } else {
                    ch
                }
            })
            .collect()
    }

The map() method accepts a closure in the form of |x|.... We then use the captured individual items that we get from chars() and process them.

If you look at the closure, you'll see we don't use the return keyword either. If we don't put the semicolon in a branch and it's the last item, it will be considered as a return value.

Using the if block is good, but we can also make it more idiomatic. One of the Rust language's strengths is the powerful match control flow.

  1. Let's change the code again:
    fn encrypt(&self) -> String {
        self.0
            .chars()
            .map(|ch| match ch {
                'a'..='m' | 'A'..='M' => (ch as u8 + 13) 
                as char,
                'n'..='z' | 'N'..='Z' => (ch as u8 - 13) 
                as char,
                _ => ch,
            })
            .collect()
    }

That looks a lot cleaner. The pipe (|) operator is a separator to match items in an arm. The Rust matcher is exhaustive, which means that the compiler will check whether all possible values of the matcher are included in the matcher or not. In this case, it means all characters in Unicode. Try removing the last arm and compiling it to see what happens if you don't include an item in a collection.

You can define a range by using .. or ..=. The former means we are excluding the last element, and the latter means we are including the last element.

  1. Now that we have implemented our simple encryptor, let's use it in our main application:
    fn main() {
        ...
        io::stdin()
        .read_line(&mut user_input)
        .expect("Cannot read input");
        println!(
            "Your encrypted string: {}",
            encryptor::rot13::Rot13(user_input).encrypt()
        );
    }

Right now, when we try to compile it, the compiler will show an error. Basically, the compiler is saying you cannot use a trait function if the trait is not in the scope, and the help from the compiler is showing what we need to do.

  1. Put the following line above the main() function and the compiler should produce a binary without any error:
    use encryptor::Encryptable;
  2. Let's try running the executable:
    > ./main
    Input the string you want to encrypt:
    asdf123
    Your encrypted string: nfqs123
    > ./main
    Input the string you want to encrypt:
    nfqs123
    Your encrypted string: asdf123

We have finished our program and we improved it with real-world encryption. In the next section, we're going to learn how to search for and use third-party libraries and incorporate them into our application.

Packages and Cargo

Now that we know how to create a simple program in Rust, let's explore Cargo, the Rust package manager. Cargo is a command-line application that manages your application dependencies and compiles your code.

Rust has a community package registry at https://crates.io. You can use that website to search for a library that you can use in your application. Don't forget to check the license of the library or application that you want to use. If you register on that website, you can use Cargo to publicly distribute your library or binary.

How do we install Cargo into our system? The good news is Cargo is already installed if you install the Rust toolchain in the stable channel using rustup.

Cargo package layout

Let's try using Cargo in our application. First, let's copy the application that we wrote earlier:

cp -r 02ComplexProgram  03Packages
cd 03Packages
cargo init . --name our_package

Since we already have an existing application, we can initialize our existing application with cargo init. Notice we add the --name option because we are prefixing our folder name with a number, and a Rust package name cannot start with a number.

If we are creating a new application, we can use the cargo new package_name command. To create a library-only package instead of a binary package, you can pass the --lib option to cargo new.

You will see two new files, Cargo.toml and Cargo.lock, inside the folder. The .toml file is a file format commonly used as a configuration file. The lock file is generated automatically by Cargo, and we don't usually change the content manually. It's also common to add Cargo.lock to your source code versioning application ignore list, such as .gitignore, for example.

Let's check the content of the Cargo.toml file:

[package]
name = "our_package"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at
https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[[bin]]
name = "our_package"
path = "main.rs"

As you can see, we can define basic things for our application such as name and version. We can also add important information such as authors, homepage, repository, and much more. We can also add dependencies that we want to use in the Cargo application.

One thing that stands out is the edition configuration. The Rust edition is an optional marker to group various Rust language releases that have the same compatibility. When Rust 1.0 was released, the compiler did not have the capability to know the async and await keywords. After async and await were added, it created all sorts of problems with older compilers. The solution to that problem was to introduce Rust editions. Three editions have been defined: 2015, 2018, and 2021.

Right now, the Rust compiler can compile our package perfectly fine, but it is not very idiomatic because a Cargo project has conventions on file and folder names and structures. Let's change the files and directory structure a little bit:

  1. A package is expected to reside in the src directory. Let's change the Cargo.toml file [[bin]] path from "main.rs" to "src/main.rs".
  2. Create the src directory inside our application folder. Then, move the main.rs file and the encryptor folder to the src folder.
  3. Add these lines to Cargo.toml after [[bin]]:
    [lib]
    name = "our_package"
    path = "src/lib.rs"
  4. Let's create the src/lib.rs file and move this line from src/main.rs to src/lib.rs:
    pub mod encryptor;
  5. We can then simplify using both the rot13 and Encryptable modules in our main.rs file:
    use our_package::encryptor::{rot13, Encryptable};
    use std::io;
    fn main() {
        ...
        println!(
            "Your encrypted string: {}",
            rot13::Rot13(user_input).encrypt()
        );
    }
  6. We can check whether there's an error that prevents the code from being compiled by typing cargo check in the command line. It should produce something like this:
    > cargo check
        Checking our_package v0.1.0 
        (/Users/karuna/Chapter01/03Packages)
        Finished dev [unoptimized + debuginfo] target(s) 
        in 1.01s
  7. After that, we can build the binary using the cargo build command. Since we didn't specify any option in our command, the default binary should be unoptimized and contain debugging symbols. The default location for the generated binary is in the target folder at the root of the workspace:
    $ cargo build
       Compiling our_package v0.1.0 
       (/Users/karuna/Chapter01/03Packages)
        Finished dev [unoptimized + debuginfo] target(s) 
        in 5.09s

You can then run the binary in the target folder as follows:

./target/debug/our_package

debug is enabled by the default dev profile, and our_package is the name that we specify in Cargo.toml.

If you want to create a release binary, you can specify the --release option, cargo build --release. You can find the release binary in ./target/release/our_package.

You can also type cargo run, which will compile and run the application for you.

Now that we have arranged our application structure, let's add real-world encryption to our application by using a third-party crate.

Using third-party crates

Before we implement another encryptor using a third-party module, let's modify our application a little bit. Copy the previous 03Packages folder to the new folder, 04Crates, and use the folder for the following steps:

  1. We will rename our Encryptor trait as a Cipher trait and modify the functions. The reason is that we only need to think about the output of the type, not the encrypt process itself:
    • Let's change the content of src/lib.rs to pub mod cipher;.
    • After that, rename the encryptor folder as cipher.
    • Then, modify the Encryptable trait into the following:
      pub trait Cipher {
          fn original_string(&self) -> String;
          fn encrypted_string(&self) -> String;
      }

The reality is we only need functions to show the original string and the encrypted string. We don't need to expose the encryption in the type itself.

  1. After that, let's also change src/cipher/rot13.rs to use the renamed trait:
    impl super::Cipher for Rot13 {
        fn original_string(&self) -> String {
            String::from(&self.0)
        }
        fn encrypted_string(&self) -> String {
            self.0
                .chars()
                .map(|ch| match ch {
                    'a'..='m' | 'A'..='M' => (ch as u8 + 
                    13) as char,
                    'n'..='z' | 'N'..='Z' => (ch as u8 – 
                    13) as char,
                    _ => ch,
                })
                .collect()
        }
    }
  2. Let's also modify main.rs to use the new trait and function:
    use our_package::cipher::{rot13, Cipher};
    …
    fn main() {
        …
        println!(
            "Your encrypted string: {}",
            rot13::Rot13(user_input).encrypted_string()
        );
    }

The next step is to determine what encryption and library we want to use for our new type. We can go to https://crates.io and search for an available crate. After searching for a real-world encryption algorithm on the website, we found https://crates.io/crates/rsa. We found that the RSA algorithm is a secure algorithm, the crate has good documentation and has been audited by security researchers, the license is compatible with what we need, and there's a huge number of downloads. Aside from checking the source code of this library, all indications show that this is a good crate to use. Luckily, there's an install section on the right side of that page. Besides the rsa crate, we are also going to use the rand crate, since the RSA algorithm requires a random number generator. Since the generated encryption is in bytes, we must encode it somehow to string. One of the common ways is to use base64.

  1. Add these lines in our Cargo.toml file, under the [dependencies] section:
    rsa = "0.5.0"
    rand = "0.8.4"
    base64 = "0.13.0"
  2. The next step should be adding a new module and typing using the rsa crate. But, for this type, we want to modify it a little bit. First, we want to create an associated function, which might be called a constructor in other languages. We want to then encrypt the input string in this function and store the encrypted string in a field. There's a saying that all data not in processing should be encrypted by default, but the fact is that we as programmers rarely do this.

Since RSA encryption is dealing with byte manipulation, there's a possibility of errors, so the return value of the associated function should be wrapped in the Result type. There's no compiler rule, but if a function cannot fail, the return should be straightforward. Regardless of whether or not a function can produce a result, the return value should be Option, but if a function can produce an error, it's better to use Result.

The encrypted_string() method should return the stored encrypted string, and the original_string() method should decrypt the stored string and return the plain text.

In src/cipher/mod.rs, change the code to the following:

pub trait Cipher {
    fn original_string(&self) -> Result<String, 
    Box<dyn Error>>;
    fn encrypted_string(&self) -> Result<String, 
    Box<dyn Error>>;
}
  1. Since we changed the definition of the trait, we have to change the code in src/cipher/rot13.rs as well. Change the code to the following:
    use std::error::Error;
    pub struct Rot13(pub String);
    impl super::Cipher for Rot13 {
        fn original_string(&self) -> Result<String, 
        Box<dyn Error>> {
            Ok(String::from(&self.0))
        }
        fn encrypted_string(&self) -> Result<String, 
        Box<dyn Error>> {
            Ok(self
                .0
                ...
                .collect())
        }
    }
  2. Let's add the following line in the src/cipher/mod.rs file:
    pub mod rsa;
  3. After that, create rsa.rs inside the cipher folder and create the Rsa struct inside it. Notice that we use Rsa instead of RSA as the type name. The convention is to use CamelCase for type:
    use std::error::Error;
    pub struct Rsa {
        data: String,
    }
    impl Rsa {
        pub fn new(input: String) -> Result<Self, Box<
        dyn Error>> {
            unimplemented!();
        }
    }
    impl super::Cipher for Rsa {
        fn original_string(&self) -> Result<String, ()> {
           unimplemented!();
        }
        fn encrypted_string(&self) -> Result<String, ()> {
            Ok(String::from(&self.data))
        }
    }

There are a couple of things we can observe. The first one is the data field does not have the pub keyword since we want to make it private. You can see that we have two impl blocks: one is for defining the methods of the Rsa type itself, and the other is for implementing the Cipher trait.

Also, the new() function does not have self, mut self, &self, or &mut self as the first parameter. Consider it as a static method in other languages. This method is returning Result, which is either Ok(Self) or Box<dyn Error>. The Self instance is the instance of the Rsa struct, but we'll discuss Box<dyn Error> later when we talk about error handling in Chapter 7, Handling Errors in Rust and Rocket. Right now, we haven't implemented this method, hence the usage of the unimplemented!() macro. Macros in Rust look like a function but with an extra bang (!).

  1. Now, let's implement the associated function. Modify src/cipher/rsa.rs:
    use rand::rngs::OsRng;
    use rsa::{PaddingScheme, PublicKey, RsaPrivateKey};
    use std::error::Error;
    const KEY_SIZE: usize = 2048;
    pub struct Rsa {
        data: String,
        private_key: RsaPrivateKey,
    }
    impl Rsa {
         pub fn new(input: String) -> Result<Self, Box<
        dyn Error>> {
            let mut rng = OsRng;
            let private_key = RsaPrivateKey::new(&mut rng, 
            KEY_SIZE)?;
            let public_key = private_key.to_public_key();
            let input_bytes = input.as_bytes();
            let encrypted_data =
                public_key.encrypt(&mut rng, PaddingScheme
                ::new_pkcs1v15_encrypt(), input_bytes)?;
            let encoded_data = 
            base64::encode(encrypted_data);
            Ok(Self {
                data: encoded_data,
                private_key,
            })
        }
    }

The first thing we do is declare the various types we are going to use. After that, we define a constant to denote what size key we are going to use.

If you understand the RSA algorithm, you already know that it's an asymmetric algorithm, meaning we have two keys: a public key and a private key. We use the public key to encrypt data and use the private key to decrypt the data. We can generate and give the public key to the other party, but we don't want to give the private key to the other party. That means we must store the private key inside the struct as well.

The new() implementation is pretty straightforward. The first thing we do is declare a random number generator, rng. We then generate the RSA private key. But, pay attention to the question mark operator (?) on the initialization of the private key. If a function returns Result, we can quickly return the error generated by calling any method or function inside it by using (?) after that function.

Then, we generate the RSA public key from a private key, encode the input string as bytes, and encrypt the data. Since encrypting the data might have resulted in an error, we use the question mark operator again. We then encode the encrypted bytes as a base64 string and initialize Self, which means the Rsa struct itself.

  1. Now, let's implement the original_string() method. We should do the opposite of what we do when we create the struct:
    fn original_string(&self) -> Result<String, Box<dyn Error>> {
        let decoded_data = base64::decode(&self.data)?;
        let decrypted_data = self
            .private_key
            .decrypt(PaddingScheme::
            new_pkcs1v15_encrypt(), &decoded_data)?;
        Ok(String::from_utf8(decrypted_data)?)
    }

First, we decode the base64 encoded string in the data field. Then, we decrypt the decoded bytes and convert them back to a string.

  1. Now that we have finished our Rsa type, let's use it in our main.rs file:
    fn main() {
        ...
        println!(
            "Your encrypted string: {}",
            rot13::Rot13(user_input).encrypted_
            string().unwrap()
        );
        println!("Input the string you want to encrypt:");
        let mut user_input = String::new();
        io::stdin()
            .read_line(&mut user_input)
            .expect("Cannot read input");
        let encrypted_input = rsa::Rsa::new(
        user_input).expect("");
        let encrypted_string = encrypted_input.encrypted_
        string().expect("");
        println!("Your encrypted string: {}", 
        encrypted_string);
        let decrypted_string = encrypted_input
        .original_string().expect("");
        println!("Your original string: {}", 
        decrypted_string);
    }

Some of you might wonder why we redeclared the user_input variable. The simple explanation is that Rust already moved the resource to the new Rot13 type, and Rust does not allow the reuse of the moved value. You can try commenting on the second variable declaration and compile the application to see the explanation. We will discuss the Rust borrow checker and moving in more detail in Chapter 9, Displaying Users' Post.

Now, try running the program by typing cargo run:

$ cargo run
   Compiling cfg-if v1.0.0
   Compiling subtle v2.4.1
   Compiling const-oid v0.6.0
   Compiling ppv-lite86 v0.2.10
   ...
   Compiling our_package v0.1.0 
   (/Users/karuna//Chapter01/04Crates)
    Finished dev [unoptimized + debuginfo] target(s) 
    in 3.17s
     Running `target/debug/our_package`
Input the string you want to encrypt:
first
Your encrypted string: svefg
Input the string you want to encrypt:
second
Your encrypted string: lhhb9RvG9zI75U2VC3FxvfUujw0cVqqZFgPXhNixQTF7RoVBEJh2inn7sEefDB7eNlQcf09lD2nULfgc2mK55ZE+UUcYzbMDu45oTaPiDPog4L6FRVpbQR27bkOj9Bq1KS+QAvRtxtTbTa1L5/OigZbqBc2QOm2yHLCimMPeZKhLBtK2whhtzIDM8l5AYTBg+rA688ZfB7ZI4FSRm4/h22kNzSPo1DECI04ZBprAq4hWHxEKRwtn5TkRLhClGFLSYKkY7Ajjr3EOf4QfkUvFFhZ0qRDndPI5c9RecavofVLxECrYfv5ygYRmW3B1cJn4vcBhVKfQF0JQ+vs+FuTUpw==
Your original string: second

You will see that Cargo automatically downloaded the dependencies and builds them one by one. Also, you might notice that encrypting using the Rsa type took a while. Isn't Rust supposed to be a fast system language? The RSA algorithm itself is a slow algorithm, but that's not the real cause of the slowness. Because we are running the program in a development profile, the Rust compiler generates an application binary with all the debugging information and does not optimize the resulting binary. On the other hand, if you build the application using the --release flag, the compiler generates an optimized application binary and strips the debugging symbols. The resulting binary compiled with the release flag should execute faster than the debug binary. Try doing it yourself so you'll remember how to build a release binary.

In this section, we have learned about Cargo and third-party packages, so next, let's find out where to find help and documentation for the tools that we have used.

Tools and getting help

Now that we have created a pretty simple application, you might be wondering what tools we can use for development, and how to find out more about Rust and get help.

Tools

Besides Cargo, there are a couple more tools we can use for Rust application development:

  • rustfmt

This program is for formatting your source code so it follows the Rust style guide. You can install it by using rustup (rustup component add rustfmt). Then, you can integrate it with your favorite text editor or use it from the command line. You can read more about rustfmt at https://github.com/rust-lang/rustfmt.

  • clippy

Does the name remind you of something? clippy is useful for linting your Cargo application using various lint rules. Right now, there are more than 450 lint rules you can use. You can install it using this command: rustup component add clippy. Afterward, you can use it in the Cargo application by running cargo clippy. Can you try it in the Cargo application that we wrote earlier? You can read more about clippy at https://github.com/rust-lang/rust-clippy.

Text editor

Most likely, the text editor of your choice already supports the Rust language, or at least syntax highlighting Rust. You can install the Rust language server if you want to add important functionalities such as go to definition, go to implementation, symbol search, and code completion. Most popular text editors already support the language server, so you can just install an extension or other integration method to your text editor:

  • The Rust language server

You can install it using the rustup command: rustup component add rls rust-analysis rust-src. Then, you can integrate it into your text editor. For example, if you are using Visual Studio Code, you can install the Rust extension and enable rls.

You can read more about it at https://github.com/rust-lang/rls.

  • Rust analyzer

This application is poised to be the Rust language server 2.0. It's still considered to be in alpha as of the writing of this book, but in my experience, this application works well with regular updates. You can find the executable for this one at https://github.com/rust-analyzer/rust-analyzer/releases, and then configure your editor language server to use this application. You can read more about it at https://rust-analyzer.github.io.

Getting help and documentation

There are a few important documents that you might want to read to find help or references:

  • The Rust programming language book: This is the book that you want to read if you want to understand more about the Rust programming language. You can find it online at https://doc.rust-lang.org/book/.
  • Rust by Example: This documentation is a collection of small examples that show the concepts of the Rust language and its standard library's capabilities. You can read it online at https://doc.rust-lang.org/rust-by-example/index.html.
  • Standard library documentation: As a programmer, you will refer to this standard library documentation. You can read more about standard libraries, their modules, the function signatures, what standard libraries' functions do, read the examples, and more. Find it at https://doc.rust-lang.org/std/index.html.
  • The Cargo book: If you are interested in Cargo and related information such as the Cargo.toml manifest format, you can read more about it at https://doc.rust-lang.org/cargo/index.html.
  • Rust style guidelines: The Rust language, like other programming languages, has style guidelines. These guidelines tell a programmer what the convention for naming is, about whitespaces, how to use constants, and other idiomatic conventions for a Rust program. Read more about it at https://doc.rust-lang.org/1.0.0/style/.
  • Docs.rs: Suppose you are using a third-party crate, such as the rsa crate that we used earlier. To find documentation for that library, you can go to https://crates.io and search for the crate's page, then go to the right pane and go to the documentation section. Or, you can go to https://docs.rs and search for the crate name and find the documentation for it.
  • Rustup doc: This documentation is not online, but you can install it using rustup (rustup component add rust-docs). Then, you can open documentation in your browser while offline using the rustup doc command. If you want to open standard library documentation offline, you can type rustup doc --std. There are other documents you can open; try and see what they are by using rustup doc --help.
  • The Rust user forum: If you want to get help or help other Rust programmers, you can find it all over the internet. There's a dedicated forum to discuss Rust-related topics at https://users.rust-lang.org/.

Summary

In this chapter, we had a brief overview of the Rust language. We learned about the Rust toolchain and how to install it as well as the tools required for Rust development. After that, we created two simple programs, used Cargo, and imported third-party modules to improve our program. Now that you can write a small program in the Rust language, explore! Try creating more programs or experimenting with the language. You can try Rust by Example to see what features we can use in our programs. In subsequent chapters, we will learn more about Rocket, a web framework written in the Rust language.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Discover solutions to the common problems faced while creating web applications with Rocket
  • Learn everything about Rust, from structs and crates to generics and debugging
  • Combine Rust and Rocket to create, test, and deploy a full-featured web app

Description

Looking for a fast, powerful, and intuitive framework to build web applications? This Rust book will help you kickstart your web development journey and take your Rust programming skills to the next level as you uncover the power of Rocket - a fast, flexible, and fun framework powered by Rust. Rust Web Development with Rocket wastes no time in getting you up to speed with what Rust is and how to use it. You’ll discover what makes it so productive and reliable, eventually mastering all of the concepts you need to play with the Rocket framework while developing a wide set of web development skills. Throughout this book, you'll be able to walk through a hands-on project, covering everything that goes into making advanced web applications, and get to grips with the ins and outs of Rocket development, including error handling, Rust vectors, and wrappers. You'll also learn how to use synchronous and asynchronous programming to improve application performance and make processing user content easy. By the end of the book, you'll have answers to all your questions about creating a web application using the Rust language and the Rocket web framework.

Who is this book for?

This web development book is for software engineers who want to learn how to use the Rocket framework to build web applications. Although not mandatory, basic knowledge of the Rust programming language will help you understand the topics covered easily.

What you will learn

  • Master the basics of Rust, such as its syntax, packages, and tools
  • Get to grips with Rocket s tooling and ecosystem
  • Extend your Rocket applications using Rust and third-party libraries
  • Create a full-fledged web app with Rocket that handles user content
  • Write pattern-matching logic and handle Rust object lifetimes
  • Use APIs and async programming to make your apps secure and reliable
  • Test your Rocket application and deploy it to production
  • Containerize and scale your applications for maximum efficiency
Estimated delivery fee Deliver to Portugal

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jun 30, 2022
Length: 420 pages
Edition : 1st
Language : English
ISBN-13 : 9781800561304
Languages :
Concepts :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
Estimated delivery fee Deliver to Portugal

Premium delivery 7 - 10 business days

€17.95
(Includes tracking information)

Product Details

Publication date : Jun 30, 2022
Length: 420 pages
Edition : 1st
Language : English
ISBN-13 : 9781800561304
Languages :
Concepts :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
€18.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
€189.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts
€264.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just €5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total 95.97
Practical WebAssembly
€38.99
Game Development with Rust and WebAssembly
€32.99
Rust Web Development with Rocket
€23.99
Total 95.97 Stars icon

Table of Contents

19 Chapters
Part 1: An Introduction to the Rust Programming Language and the Rocket Web Framework Chevron down icon Chevron up icon
Chapter 1: Introducing the Rust Language Chevron down icon Chevron up icon
Chapter 2: Building Our First Rocket Web Application Chevron down icon Chevron up icon
Chapter 3: Rocket Requests and Responses Chevron down icon Chevron up icon
Chapter 4: Building, Igniting, and Launching Rocket Chevron down icon Chevron up icon
Chapter 5: Designing a User-Generated Application Chevron down icon Chevron up icon
Part 2: An In-Depth Look at Rocket Web Application Development Chevron down icon Chevron up icon
Chapter 6: Implementing User CRUD Chevron down icon Chevron up icon
Chapter 7: Handling Errors in Rust and Rocket Chevron down icon Chevron up icon
Chapter 8: Serving Static Assets and Templates Chevron down icon Chevron up icon
Chapter 9: Displaying Users' Post Chevron down icon Chevron up icon
Chapter 10: Uploading and Processing Posts Chevron down icon Chevron up icon
Chapter 11: Securing and Adding an API and JSON Chevron down icon Chevron up icon
Part 3: Finishing the Rust Web Application Development Chevron down icon Chevron up icon
Chapter 12: Testing Your Application Chevron down icon Chevron up icon
Chapter 13: Launching a Rocket Application Chevron down icon Chevron up icon
Chapter 14: Building a Full Stack Application Chevron down icon Chevron up icon
Chapter 15: Improving the Rocket Application Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Full star icon Half star icon 4.3
(6 Ratings)
5 star 66.7%
4 star 16.7%
3 star 0%
2 star 16.7%
1 star 0%
Filter icon Filter
Top Reviews

Filter reviews by




Drew Osmond Aug 20, 2022
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This book walks you through each step of building a web server, and is a good intro to the rust programming language. Coming from a nodejs background, this book made me realize I could start branching out to other backend stacks.
Amazon Verified review Amazon
elliottwins Aug 22, 2022
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Rocket is one of the most full featured web frameworks for Rust. Due to the amount of features it offers, Rocket can be overwhelming to take on for someone looking at getting into Rust web development. This book does a fantastic job of guiding the reader throughout the many different concepts found in Rocket, helping to dispel and explain some of the magic found under the hood. The book covers areas that are vital for being able to build a functional web app, such as middleware, using fairings, DB connections using SQLx and asynchronous processing using tokio.If you're looking to get started with building web apps using Rust, this book is a great resource to gain an understanding of how to do so using Rocket.
Amazon Verified review Amazon
L. Smith Aug 30, 2022
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Goods books (or any books really) on Rust web frameworks are few and far between. I haven't read the entire book but I did sit down and devote 3 hours or so to it and read through page 189 (of 420 pages). It's well written and straight forward. Literally anyone who has done any coding (from simple JS or PHP to complicated C++) should be able grasp the information presented in this book and probably start use the Rocket web framework for Rust within hours if they want to try it out (for hobby or for serious projects / work). I am so glad I had the opportunity the check this book out!
Amazon Verified review Amazon
Amazon Customer Oct 10, 2022
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I like how book builds upon one large project that includes generating Docker images, connecting to a database and building a full stack app in Rust. Well written! #RustLang #Postgresql
Amazon Verified review Amazon
Geraldo Netto Sep 30, 2022
Full star icon Full star icon Full star icon Full star icon Empty star icon 4
As always, a bit of contextRust can cover a wide number of cases, from system tools (even operating systems (redox) and virtual machines (firecracker from Amazon/AWS)) to webappsIt can do the same as java/go with more "fences" (e.g.: thread-safety by design) and/or performance (no GC as in java/go)Rocket is a mature web framework with more or less 5 ~ 6 years of existence, now developed/kept by the communityIt's on par with other popular web frameworks from other languagesAlso, it is based on Tokio - probably the most used/trusted framework for network applications (http, grpc, sockets, ...) in rustThe book starts with a quick introduction to rust which can be useful if you don't know rustIt slowly develops rust programming with the rocket frameworkIt also covers the usage of templates and webassembly which are part of any modern web applicationSome highlights:- covers rocket structure and lifecycle- explains how to use databases with rocket through the crate sqlx-cli (CRUD)- explains the usage of templating- discuss some best practices (e.g.: how to avoid Insecure Direct Object References)- WebAssembly- json handling- testingTo summarize, it is a good introduction to the usage of rocketThe only thing I wish I could be done in a different way is that some rust programming details like borrowing are not explained in the introduction to rust but instead diluted through the bookI think this could be part of the first chapter and for those who are already experienced with rust, could skip the introduction to rust chapter altogether
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela