Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Rust Programming By Example

You're reading from   Rust Programming By Example Enter the world of Rust by building engaging, concurrent, reactive, and robust applications

Arrow left icon
Product type Paperback
Published in Jan 2018
Publisher Packt
ISBN-13 9781788390637
Length 454 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Antoni Boucher Antoni Boucher
Author Profile Icon Antoni Boucher
Antoni Boucher
Guillaume Gomez Guillaume Gomez
Author Profile Icon Guillaume Gomez
Guillaume Gomez
Arrow right icon
View More author details
Toc

Table of Contents (13) Chapters Close

Preface 1. Basics of Rust 2. Starting with SDL FREE CHAPTER 3. Events and Basic Game Mechanisms 4. Adding All Game Mechanisms 5. Creating a Music Player 6. Implementing the Engine of the Music Player 7. Music Player in a More Rusty Way with Relm 8. Understanding FTP 9. Implementing an Asynchronous FTP Server 10. Implementing Asynchronous File Transfer 11. Rust Best Practices 12. Other Books You May Enjoy

References

Let's try the following code, which would work in other programming languages:

let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("{}", p1.x);

We can see that Rust doesn't accept this. It gives the following error:

error[E0382]: use of moved value: `p1.x`
 --> src/main.rs:4:20
  |
3 |     let p2 = p1;
  |         -- value moved here
4 |     println!("{}", p1.x);
  |                    ^^^^ value used here after move
  |
  = note: move occurs because `p1` has type `Point`, which does not implement the `Copy` trait

This means that we cannot use a value after it is moved. In Rust, values are moved by default instead of being copied, except in some cases, as we'll see in the next sub-section.

To avoid moving a value, we can take a reference to it by prefixing it with &:

let p1 = Point { x: 1, y: 2 };
let p2 = &p1;
println!("{}", p1.x);

This code compiles and, in this case, p2 is a reference to p1, which means that it points to the same memory location. Rust ensures that it is always safe to use a reference, since references are not pointers, they cannot be NULL.

References can also be used in the type of a function parameter. This is a function that prints a point, without moving the value:

fn print_point(point: &Point) {
    println!("x: {}, y: {}", point.x, point.y);
}

We can use it this way:

print_point(&p1);
println!("{}", p1.x);

We can still use the point after calling print_point, because we send a reference to the function instead of moving the point into the function.

Clone types

An alternative to using references is to clone values. By cloning a value, we don't move it. To be able to clone a point, we can add derive to it:

#[derive(Clone, Debug)]
struct Point {
    x: i32,
    y: i32,
}

We can now call the clone() method to avoid moving our p1 point:

fn print_point(point: Point) {
    println!("x: {}, y: {}", point.x, point.y);
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();
print_point(p1.clone());
println!("{}", p1.x);

Copy types

Some types are not moved when we assigned a value of these types to another variable. This is the case for basic types such as integers. For instance, the following code is perfectly valid:

let num1 = 42;
let num2 = num1;
println!("{}", num1);

We can still use num1 even thought we assigned it to num2. This is because the basic types implement a special marker: Copy. Copy types are copied instead of moved.

We can make our own types Copy by adding derive to them:

#[derive(Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

Since Copy requires Clone, we also implement the latter for our Point type. We cannot derive Copy for a type containing a value that does not implement Copy. Now, we can use a Point without having to bother with references:

fn print_point(point: Point) {
    println!("x: {}, y: {}", point.x, point.y);
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1;
print_point(p1);
println!("{}", p1.x);

Mutable references

If we want to be able to mutable thought a reference, we need a mutable reference, since everything is immutable by default in Rust. To get a mutable reference, simply replace & with &mut. Let's write a function that will increment the x field of a Point:

fn inc_x(point: &mut Point) {
    point.x += 1;
}

Here, we see that the Point type is now &mut, which allows us to update the point in the method. To use this method, our p1 variable needs to be mut and we also need to take a mutable reference for this variable:

let mut p1 = Point { x: 1, y: 2 };
inc_x(&mut p1);
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime