Official website for Linux User & Developer
FOLLOW US ON:
Aug
24

Programming in Rust

by Mihalis Tsoukalos

Discover Rust, the systems programming language developed by Mozilla that’s fast, and wants to be better than C and C++!

Rust is a relatively new systems programming language that aims to avoid unpleasant bugs caused by unsafe code. Those of you who have experience with C or C++ will likely find the syntax of Rust quite familiar.

While Rust uses many familiar concepts from other mainstream programming languages, it also borrows elements from research languages including ‘owning pointers’ and ‘borrowed pointers’. Currently Rust is changing quite fast but still, we think the best way to learn programming with Rust is to just get hands-on and try writing some programs. All you need to get going is the Rust compiler and your favourite text editor. The Rust compiler is ingenious and able to find many errors as it’s responsible for enforcing the safety rules, preventing your Rust code from compiling until you make the necessary corrections to your code.

If after finishing this article you are hungry for more information, you will find Rust’s website (www.rust-lang.org) a good place to continue your learning. In the meantime, let’s get started with the installation!

Learn to program in Rust, Mozilla's new language
Learn to program in Rust, Mozilla’s new language

Resources

Rust

Rustdoc

Getting Rust

Step-by-step

Step 01 Install Rust

As Debian 7 does not have a package ready for installation, a manual installation of Rust is required. The Rust site has precompiled binaries for most operating systems that you can download. After having extracted the Rust archive, you can then install Rust by simply running the install. sh script using root privileges.

The default installation place for the Rust compiler is the /usr/local/bin directory. The name of the compiler executable is rustc. Rust also installs another tool, called rustdoc, which is used for generating documentation from all of the Rust source files.

Step 02 Compile your first Rust program

The Rust code for the Hello World program is the following:

fn main() {
println!(“Hello World!”);
}

By convention, Rust code is saved in files that have an ‘.rs’ extension, so the “Hello World!” program can be saved as ‘hw.rs’. You can compile the hw.rs source file as follows:

$ rustc hw.rs

If you get the following error messages while trying to compile hw.rs…

error: could not exec the linker `cc`: no such file or directory
error: aborting due to previous error

… it means that you do not have a linker installed. You can solve the problem by installing the gcc package and its dependencies. If everything is okay, Rust will automatically create an executable file called ‘hw’ that you can run as usual.

Step 03 Basic language features

You can declare a new variable with the keyword let and optionally give a type to it. The compiler will then try and find out the type but if it fails, it will give you an error message. It is considered good practice to always give a type to a variable, as they are immutable by default. In order to introduce a local variable whose value you are able to change later, you should use the let mut keywords.

An identifier followed by an exclamation point, such as println!, is a macro invocation. Macros are used in Rust because they provide syntactic abstraction. You can find your version of Rust by executing the following command:

$ rustc -v

Bear in mind that Rust is currently under heavy development, so the Rust version could well be different to the one we’re using here.

Step 04 Get user input and print output

Printing output can be done with the println! macro. This macro is similar in functionality to the printf function found in C. Reading a value can be done with the help of the io::stdin().read_line().unwrap() function as follows:

println!(“Give me the price”);
let price = io::stdin().read_line().unwrap();
println!(“The price is {}”, price);

It may look a little strange to begin with but you will get used to it soon enough – after all, you are learning a new programming language! The unwrap() part of the io::stdin(). read_line().unwrap() function deletes the OK value that is added in by Rust in the price variable as an indication that it was read without any problems.

Step 05 Pointers, freezing and borrowing

Rust also supports pointers – although you might be happy to learn that you don’t need to use them very often. Fully explaining pointers is beyond the scope of this article, but it’s good to know that they exist. Although Rust enforces safety while dealing with pointers, it also allows you to break this rule using an unsafe block, implying that you know what are you doing (trying to make your program run faster or because it is required by the task you are trying to implement). You can think of an unsafe block like programming in C using pointers, memory allocation and so on.

Rust also supports freezing: putting an ‘&’ in front of an object freezes it and forbids mutation – even if the object was declared as mut. ‘Frozen’ data cannot be modified via the original object, until all the references to it go out of scope. When data is borrowed, it automatically freezes. Borrowing allows you to access some data, without taking ownership over it. In Rust, resources can only have one owner, who can free or move the memory. The following code illustrates the freezing of data:

letmutlud=5;
{
let y = &lud; // ‘lud’ is now frozen.
}
// ‘lud’ is now unfrozen again

Trying to modify a frozen variable will cause the compiler to generate an error.

Step 06 Functions in Rust

Functions are declared either at the top level or inside other functions. The fn keyword introduces a function. A function has an argument list, which is a parenthesised list of ‘name: type’ pairs separated by commas. An arrow separates the argument list and the return type of the function. Every autonomous Rust program must have a main() function, as it happens in C and C++.

By default, the final expression in the function will be used as its return value, unless a semicolon terminates the expression.

Step 07 Expressions

Rust allows you to use the following unique-to-Rust syntax in order to set the value of the discount variable:

let discount =
   if intPrice <= 5 {
      10
   } else if intPrice <= 20 {
      15
   } else { 
      25
   };

Its main advantages are that it is less error prone as well as being easier to read and understand. Nevertheless, it is not mandatory and you can still use the common syntax instead:

let discount;
if intPrice <= 5 {
   discount = 10;
} else if intPrice <= 20 {
   discount = 15;
} else {
   discount = 25;
}

Step 08 Implement a function that calculates Fibonacci numbers

The implementation of the function that calculates Fibonacci numbers in Rust uses recursion as usual. The program accepts its input from the command line and uses various Rust features, including access to the command line arguments and vectors. The Fibonacci function is declared as follows:

fn fibonacci(n: int) -> int {...€¦

This declaration tells that the Fibonacci function takes an integer as its input and then returns an integer as its output.

The access to the vector that holds the command line arguments – skipping the first value because it’s the name of the executable – is done using the following for loop:

for x in args.iter().skip(1) {...€¦}

Step 09 Pattern matching

Rust uses the match keyword to do its pattern-matching tasks. match is similar to the switch construct found in C. You provide it with a value and a number of arms – each arm has a pattern – and the code compares the value against each pattern until it finds a match. An example is the following:

let database =
   match sqlite::open(“lud.db”) {
      Ok(db) => db,
      Err(e) => {
         println!(“Cannot open lud.db!”);
         return;
      }
   };

If the sqlite::open(“lud.db”) command returns Ok(a_database_name), then the code in the first arm is executed, giving the database variable the value of the db variable. Otherwise, an error message is printed (‘Cannot open lud.db!’) and the code returns. All match constructs must have arms that cover every possible case.

Step 10 Process the log files

This part of the article will present a Rust program that demonstrates how Rust deals with reading and writing of files.

Algorithmically speaking, the program will read the log file line by line, while trying to see if the current line contains the desired string and, if yes, it will write the line to a new file. Reading a text file line by line requires the following code:

let path = Path::new(“logFile.log”);
let mut file = BufferedReader::new (File::open(&path));
for line in file.lines()
{
   print!(“{}”, line.unwrap());
}

Meanwhile, creating a new file for writing requires the following code snippet:

let lud = Path::new(outputFile);
   let mut fileOutput = match File::open_mode(&lud, Open, Write)
   {
      Ok(file) => file,
      Err(e) => fail!("file error: {}", e),
};

If you want to write a line to the file, you can do so with the following Rust code:

let result = fileOutput.write_ line(myLine);

The fileOutput file descriptor will be closed automatically on the exit of the scope, so there is no close() method.

At this stage, if you choose to run the ApacheLog executable you will get output that looks like the following:

$ ./ApacheLogs
Line 3 Found! Some(12)
Total lines processed: 5
Total lines written: 1

Step 11 Document Rust code

Rust comes with a command line tool called rustdoc that helps you generate documentation, provided that you include some documentation-related lines inside your code.

You will have to add the following lines before the use statements of ‘ApacheLogs.rs’ to make rustdoc generate meaningful output:

#![crate_id = “LUD”]
#![crate_type=“Logs”]

//! A simple tool for examining
// Apache Log files

In order to generate the HTML documentation for the modified ApacheLogs.rs file, you just need to execute the following command:

$ rustdoc ApacheLogs.rs

Using rustdoc does require some practice – as with learning any language – nevertheless, it can provide you with elegant HTML output that is more than worth the learning curve.

Tags: , ,
  • Tell a Friend
  • Follow our Twitter to find out about all the latest Linux news, reviews, previews, interviews, features and a whole more.
    • MuslimAussie

      Rust allows you to use the following unique-to-Rust syntax in order to set the value of the discount variable:

      Really? Scala has had this syntax long before rust was ever envisioned.

    • Keith McNeill

      Do we really need yet another language?

      “Rust is a relatively new systems programming language that aims to avoid unpleasant bugs caused by unsafe code.”

      Is there really no other language already filling that niche?

    • RogerV

      Rust has compile-time protections on use of pointers (and also thread synchronization locks too) via its ownership and borrowing language intrinsic behaviors. Is basically a language that can get down to the efficiency of C while avoiding complexity of C++ and with a lot of modern characteristics as expected in 21st century programming languages. It also takes an approach to where programmer is in control of memory management choices. You have to opt into using garbage collected memory. Choose a suitable implementation library (ref counted, Mark and sweep, and/or generational) and then that is meshed with language features.

    • Heide Shumway

      Is Rust a third generation language and why or why not?