Rust Introduction: Cargo, Crates, Rust project, Hello Word, (2h on computers),
Pierre Cochard, Tanguy Risset
This course has been set up for student of the Telecommunication Department at INSA-Lyon (5th year), it is vastly inspired by the Rust book and many other resource on the web. It assumes that student do not have any programming experience in Rust by have a strong programming experience in other languages (C/C++ and object languages in particular).
- Setting up the environnement for using Rust
- Rust Hello World
- Variables, Types ans Mutability
- Function in Rust
- Generic types, traits and
#derive
directive - First example of "Move" semantic: the cube
- Introduction to Visual Studio Code
- How to use VS code efficiently for Rust (TODO) {#how-to-use-vs-code-efficiently-for-rust .unnumbered}
In addition to this documents, you'll find other documents on Moodle presenting the concepts covered in this course. Don't forget to check it before you start. Many of the information listed here come from https://www.rust-lang.org/learn/.
The course is organize in sections that have questions. In addition you will find boxed text labeled course: which consists in important concept
Course: What is Rust and Why Rust
Nowadays, Rust use is growing exponentially, the number of library and project using or useful to Rust developpers is already huge. The reason for that it that Rust provides safe memory management without a garbage collector, ensuring both performance and security. Its ownership system eliminates data races and segmentation faults. It enables efficient concurrent programming while guaranteeing memory safety. It is associated with a powerful modern ecosystem and and it is suited for embedded systems, system programming as well as high-performance applications. Adopted by major industry players, Rust is emerging as a reliable alternative for secure and system-level development.
Setting up the environnement for using Rust
We're going to start by setting up the environment that will enable you to program in Rust. We recommand that you use Rust on your own machine, but the environment is already installed on the departement computers.
This environment simply consists of having:
-
An editor for programming, we strongly recommend Visual Studio Code that is available on all OS, for instance here: https://visualstudio.microsoft.com/fr/downloads/. See Appendix below for an introduction to Visual Studio Code.
-
Install the
cargo
command withrustup
.
cargo
is the Rust compiler as well as the package manager and build
system for Rust. cargo
's installation and updating is itself managed
by rustup
.
Below is a summary for installing cargo
on your laptop. The original complete instruction can be found here:
https://doc.rust-lang.org/book/ch01-01-installation.html.
As a summary:
-
On linux or macOS, use the following command:
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
-
on Windows, go to https://www.rust-lang.org/tools/install and follow the instructions for installing Rust.
Course: What is cargo used for?
-
Dependency Management: Cargo manages the dependencies of a Rust project. It automatically downloads and builds the required libraries and dependencies, making it easier for developers to include external code in their projects.
-
Project Configuration: Cargo uses a file called
Cargo.toml
to configure a Rust project. This file includes information about the project, its dependencies, and various settings. -
Building and Compilation: Cargo handles the compilation process of Rust code. It can build the project, manage dependencies, and generate executable binaries. Developers can use Cargo commands like
cargo build
to compile the project orcargo run
to build and run it in one step. -
Testing: Cargo provides built-in support for testing Rust code. Developers can use the
cargo test
command to run tests defined in the project. -
Documentation: Cargo can generate and serve documentation for the project using the
cargo doc
command. This is useful for both internal and external documentation (the HTML file that you are reading has been generated bycargo doc
) -
Publishing Packages: Cargo facilitates the process of publishing Rust packages to the official package registry, called "
Crates.io.
" This makes it easy for others to discover and use Rust libraries and projects.
Rust Hello World
A Rust project is contained in a directory which has the name of the project. From now on, we suggest making a projects directory in your home directory and keeping all your Rust projects there.
We will use the cargo
command to build our first hello_world
project. Note that this is not mandatory, everything can be built by
hand, the Rust ompiler can be invoked without cargo by using the command
rustc
.
cargo new hello_world
Cargo.toml
is the project configuration
file written in the TOML (Tom's Obvious, Minimal Language) format1,
and src/main.rs
is the Rust "main" file. if anything is unclear ask
the teacher.
cargo build
Where is the generated executable file? What is the bang (!
) after
println
Course: What about println!
As your can check on the Rust Standard Library
documentation : println!
is not a
function, it is a macro.
Macros are called with a trailling bang (such as println!
), they are a
way of writing code that writes other code, which is known as
metaprogramming. Understanding and declaring Macros is quite complex and
will be seen later. But using them (such as using println!
) is usually
very easy.
Variables, Types ans Mutability
let x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
#![allow(unused)] fn main() { let x = 5; { let x = x + 1; { let x = x * 2; println!("The value of x in the inner scope is: {x}"); } } println!("The value of x is: {x}"); }
The notion of scope is quite important in Rust, a "scope" can be manipulated in the language as an object, we will see it in more detail in TD4.
Function in Rust
Functions in Rust are like in other languages. They are declared with
the keyword fn
, parameters are passed by value and an important
specificity (borrowing values) will be study on next course.
fibonacci
:
fibonacci(n: i32) -> i32
which computes element n
of the fibonacci sequence.
We will not use a
recursive solution, but rather a for
loop whose syntax will be:
for i in 2..n+1
( half-open range) and mutable variables.
We recall the definition of the fibonacci function fib
:
fib(0)=1
fib(1)=1
fib(i)=fib(i-1)+fib(i-2) for i >=
Generic types, traits and #derive
directive
As a class in C++, types can be defined and can implement different
methods. for instance, the following code defines the type Complex
as
a struct of two floats (as C++ classes, types begin with an Uppercase by
convention).
#![allow(unused)] fn main() { struct Complex { re: f32, im: f32, } fn build_complex(re: f32,im:f32)-> Complex { Complex {re,im} } let mut a = build_complex(2.3,4.0); println!("a=({},{})",a.re,a.im); a.re = a.re+1.; println!("a=({},{})",a.re,a.im); }
Course: Generic Types
As templates in C++, Rust enables the use of generic
type in function or struct, enums or methods definitions. Here is a
simple definition of a Point
structure using integer or float
coordinates:
#![allow(unused)] fn main() { struct Point<T> { x: T, y: T, } let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; }
Complex
ci dessus en utilisant un
type struct Complex<T>
utilisant un type générique
Course: Traits: Defining common behaviour
A trait defines a functionality that a particular type has and can share with other types. Traits are similar to a feature often called interfaces in other languages.
Many natural methods can be defined for any -- or at least many --
types. For instance the copy
or clone
methods (rust primitives) or
the fmt
method (of the trait std::fmt::Display
, rust standard
library) that enables to use println!
. These methods are not defined
by default when a new type is defined.
Traits are defined by the trait
keyword. By
convention they are named starting with an upper case, e.g. the trait
Clone
, it usually defines a method with the same name in lower case
(here: clone()
)
If you want to clone a Complex
, you juste have to write the
implementation of the clone method:
impl Clone for Complex {
fn clone(&self) -> Self{
Complex{re: self.re, im: self.im}
}
// Now a.clone() can be used on Complex variables
}
struct complex<T>
type defined before. You will have to use impl<T: Clone>
to ensure that the T
generic type implements the Clone trait.
By the way, do you know the difference between copy
and clone
?
Select your prefered answer:
-
Clone
is a supertrait ofCopy
2 -
Copy
is implicit, inexpensive, and cannot be re-implemented (memcpy).Clone
is explicit, may be expensive, and may be re-implemented arbitrarily. -
The main difference is that cloning is explicit. Implicit notation means move for a non-Copy type.
Course: Deriving traits
For certain traits3:
(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, etc.
),
the compiler is capable of providing basic implementations for some
traits via the #[derive] attribute (In Rust, an attribute is metadata applied to code elements like functions, structs, modules, or crates, attributes are prefixed with #
and enclosed in square brackets []
).
These traits can still be manually implemented if a more complex behavior is required.
for instance, the Clone
trait can be automatically derived for
Complex
type:
#[derive(Clone)]
struct Complex {
re: f32,
im: f32,
}
[... no need to implement Clone ...]
let a = build_complex(2.3,4.0);
let _c=a.clone()
[...]
println!
to display Complex
variables? Two methods, test both:
- implement the
std::fmt::Display
trait for typeComplex
. This will need to:
- use
std::fmt
- search for the prototype of the
Display
trait - use the macro
write!
to print fields
- Derive the
Debug
trait that includes thefmt::Display
trait and use the"{:?}
format.
First example of "Move" semantic: the cube
In this first example we define a very simple data structure, a 'Cube'
with a single field c
that indicates the size of the cube.
Cube
and prints its size
println!("My cube: {}", Cube{c:0.5});
? How to make the cube printable?
Hint: You can derive the std::fmt::Display
trait or the Debug
trait
x
assigned to a given cube, print it and then define
a second variable y
defined by let y = x;
. Then print x
again,
what is the problem?
Course: Move semantic
In Rust, move semantics refers to the ownership transfer of data from one variable to another. Rust enforces a strict ownership model where each piece of data has a single owner at a time, and ownership can be transferred (or "moved") when a value is assigned to another variable or passed as an argument to a function.
By default an assignement such as let y = x
implies a transfer of
ownership of the content of x
to y
. This ownership concept will be
studied further in next course. This limit side effects: modifying y
do not modify x
.
In order to dupplicate the cube (as it would be done in any language),
one has to clone it or to implement the Copy
trait. Deriving the
Copy
trait for Cube
changes the semantic of the assignement: the
assignement is now a copy, not a move.
x
into y
Introduction to Visual Studio Code
Visual Studio Code (often abbreviated as VS Code) is a cross-platform source code editor developed by Microsoft. It is compatible with Windows, macOS, and Linux, offering great flexibility to developers working in diverse environments. This lightweight yet powerful editor is designed to meet the needs of modern developers, providing a wide range of features.
Key Features of Visual Studio Code
Visual Studio Code stands out due to the following features:
-
Built-in support for multiple programming languages: VS Code supports a wide array of languages such as Python, JavaScript, C++, Java, and more, thanks to its extension system.
-
Extensions and customization: A vast library of extensions is available to add functionalities like debugging, version control, and language-specific tools.
-
Integrated debugger: VS Code provides an interactive debugging environment to simplify error correction in the code.
-
Version control integration: Seamless integration with Git and other version control systems allows developers to track code changes directly within the editor.
-
Integrated terminal: A terminal is available inside the editor, enabling command execution without leaving the application.
-
IntelliSense: This feature offers intelligent code completion and contextual suggestions based on syntax and variable types.
-
Cross-platform compatibility: VS Code works consistently on Windows, macOS, and Linux, ensuring a uniform user experience regardless of the operating system.
Thanks to its intuitive interface and powerful tools, Visual Studio Code has become one of the most popular editors among developers, whether they are beginners or experienced professionals. Its active community and frequent updates make it a reliable choice for addressing the evolving needs of software development.
How to use VS code efficiently for Rust (TODO)
-
installation avec
apt
sur linux, aller sur https://code.visualstudio.com/ -
lancer sur le répertoire projet
-
ajouter l'extension rust (barre de gauche, petit carrés), search rust -> install rust-analyzer
-
go to explorer
-
ctrl-shift-P pour la liste des commandes
You can have more documentation about the TOML format here: https://toml.io/en/ or here in french: https://toml.io/fr/. However, it is probably not necessary, TOML is quite simple to understand
list of derivable traits: https://doc.rust-lang.org/rust-by-example/trait/derive.html?highlight=derive#derive