Category Archives: Rust

Cargo

As part of a little project I’m working on, I’m back playing within Rust.

Whilst we can use rustc to build our code we’re more likely to use the build and package management application cargo. Let’s take a look at the core features of using cargo.

What version are you running?

To find the version of cargo, simply type

cargo --version

Creating a new project

Cargo can be used to create a minimal project which will include the .toml configuration file and the code will be written to a src folder. Cargo expects a specific layout of configuration and source as well as writing the building artifacts to the target folder.

To generate a minimal application run the following (replacing app_name with your application name)

cargo new app_name

We can also use cargo to create a minimal library using the –lib switch

cargo new lib_name --lib

Building our project

To build the project artifacts, run the following from the root of your application/library

cargo build

This command will create the binaries in the target folder. This is (by default) a debug build, to create a release build ass the –release switch i.e.

cargo build --release

Running our project

In the scenarios where we’ve generated an executable, then we can run the application by cd-ing into the target folder and running the .exe or via cargo we use

cargo run

Check your build

In some cases, where you are not really interested in generating an executable (for example) you can run the check command which will simply verify your code is valid – this will likely be faster than generating the executable and is useful where you just want to ensure your code will build

cargo check

Publishing my first Rust library to crates.io

As part of my end of 2021 project, I rewrote my F# units of measurement converters to a format which allowed me to code generate a bunch of libraries in different languages, currently including the original F#, C# (both idiomatic to their language), Java, Python, Swift, TypeScript and Rust.

I’ve got the F# and C# ones available on nuget and the TypeScript on npmjs, I wanted to try to get the Rust library onto crates.io.

How do we do this?

  • First off, check if your package name is available on crates.io, if it is then you’ll need to change your name. Rust creates do not support namespaces so you cannot reuse an existing name but prefix with your company or whatever
  • Load up https://crates.io/ and create an account, I think using GitHub to login was the only option, so that’s what I did
  • Ensure you have an email within your profile page and that it’s verified
  • You can go to the API Tokens page and create a new token and a name for the token – you’ll need to copy the API token for the publish step (and keep it for further publishes unless you wish to revoke and regenerate – obviously not too useful in CI. If you set the environment variable CARGO_REGISTRY_TOKEN to the token then cargo will use it from there.
  • Ensure your code is committed (if you’re using source control) or you can always specify the –allow-dirty option if you prefer not to commit changes
  • Ensure you lib.rs has the correct version for your library setup. Semantic versioning is expected, hence you can use -alpha or the likes on your version
  • Now run
    cargo login abcdefghijklmnopqrstuvwxyz
    

    replacing abcdefghijklmnopqrstuvwxyz with your API token

  • Next run
    cargo publish
    

If all the above steps went well you can now search crates.io for your library.

Rust modules

Rust includes a module system for grouping code into logical groups, which may include structs, implementations, other modules etc. Module also give us the ability to manage module code’s visibility.

There’s a couple of ways for declaring our modules.

Option 1

Assuming we’ve used cargo init to create our project or simply laid out our code in the same way, then we’ll have a src folder and within that we’ll create a folder named math which will become our module name. Within math we add a file named mod.rs

src
  |--- math
    |--- mod.rs
  |--- main.rs

So here’s a very basic mod.rs file

pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

and here’s the main.rs file

mod math;

fn main() {
    println!("{}", math::add(1, 2));
}

Option 2

The second option is to name the module file math.rs and this is stored at the same level as the main.rs file, i.e.

src
  |--- math.rs
  |--- main.rs

Nested Modules

We can also nest modules (modules within modules), for example

pub mod nested {
  pub fn add(a: i32, b: i32) -> i32 {
    a + b
  }
}

// in use 

mod math;

fn main() {
    println!("{}", math::nested::add(1, 2));
}

Debugging Rust in Visual Code

I’m using Visual Code a lot for Rust development, so it’d be good to be able to debug a Rust application within it. Simply change the settings.json file, i.e.

File | Preferences | Settings

from the settings.json and now add

"debug.allowBreakpointsEverywhere": true,

Now add the C/C++ Microsoft extension if you’ve not already added it to Visual Code.

Next up, select

Debug | Add Configuration...

and the option

C++ (Windows)

Here’s my resultant launch.json file

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(Windows) Launch",
      "type": "cppvsdbg",
      "request": "launch",
      "program": "${workspaceFolder}/target/debug/test.exe",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false
    }
  ]
}

and that’s it, now add a break point to your application, select Debug | Start Debugging (F5) and you’re off and running in the debugger.

println! structs

So we’ve got ourselves a simply little struct

struct Point {
    x: i32,
    y: i32
}

We then decide that we’d like to output the current state of the Point using println!, so we write

fn main() {
    let p = Point { x: 20, y: 3 };

    println!("{}", p);
}

Running this will result in `Point` doesn’t implement `std::fmt::Display` and `Point` cannot be formatted with the default formatter. In fact we do not really need to implement std::fmt::Display, we can just annotate our struct with #[derive(Debug)] and then use the println! formatters (:? or :#?), for example

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

fn main() {
    let p = Point { x: 20, y: 3 };

    println!("{:?}", p);
}

The use of :? will result in the following output Point { x: 20, y: 3 } whereas :#? will display the values on lines of their own (a “prettier” formatter). Both :? and :#? are debug formatters, hence require the annotation #[derive(Debug)] or we can implement std::fmt::Debug, for example

impl std::fmt::Debug for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "(x is {}, y is {})", self.x, self.y)
    }
}

For situations where we simply want to create our own custom display (not just for Debug), then, as per the original error `Point` doesn’t implement `std::fmt::Display`, we would need to implement the std::fmt::Display trait, i.e.

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "(x is {}, y is {})", self.x, self.y)
    }
}

This means we no longer requiring the annotation or special formatters, hence our full code will look like this

struct Point {
    x: i32,
    y: i32
}

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "(x is {}, y is {})", self.x, self.y)
    }
}

fn main() {
    let p = Point { x: 20, y: 3 };

    println!("{}", p);
}

and as you’d expect our output is now (x is 20, y is 3).

Rust constructors

Rust doesn’t have the concept of a constructor in the sense of C++, C#, Java etc. You create new data structures by simply using the following syntax

struct Point {
   x: i32,
   y: i32
}

let pt = Point { x: 10, y: 20 };

However, by convention you might create an impl to create/initialize your structures. Rust code, by convention suggests such functions be named new. For example

impl Point {
    pub fn new() -> Point {
        Point {
            x: 0, 
            y: 0
        }
    }
}

let pt = Point::new();

Ofcourse, we might declare parameters/arguments on the function just like any other functions.

Basics of unit testing in Rust

I’m messing around with some Rust code at the moment, so expect a few posts in the near future. In this post I’ve going to jump straight into unit testing in Rust.

You don’t need to have a dependency on any unit testing frameworks as Rust has a unit testing framework integrated within it.

Our unit tests can sit along side our existing code using the conditional compilation annotation #[cfg(test)]. Let’s create a simple example test, which assumed we have a Stack implementation to test

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn initial_state() {
        let s: Stack<i32> = Stack::new();
        assert_eq!(s.length, 0);
    }
}

The following line simply allows us to use code from the parent scope (i.e. allows us to use the Stack code).

use super::*;

Next up with have the #[test] annotation which (probably fairly obviously) declares the function initial_state to be a test function.

Ofcourse we need some form of assertation code, hence the assert_eq! macro.

We can also test whether our code panics by placed the #[should_panic] annotation after the #[test] annotation. This denotes that the system under test should panic (similar to exceptions in other languages).

Some times we need to ignore a test, in such cases we can use the #[ignore] annotation

Obviously we need run our tests, we can use cargo for this, simple run

cargo test

We can also run tests in parallel using the -test-threads option, for example if we want test to be run in parallel on two threads, we use

cargo test -- --test-threads=2

See further options around controlling how your tests are run controlling how your tests are run.