Type debugging

Simple type debugging:

fn main() {
     let value = "Joe";
     let x: () = value;
     // See the compiler error.
}

Get variable type as string:

fn type_of<T>(_: &T) -> String { std::any::type_name::<T>().to_string() }

fn main() {
     let name = "Joe";

     println!("{}", type_of(&name));
}

String types

Rust’s UTF-8 strings can be borrowed as &str. This applies to both static read-only string literals and heap-allocated string objects.

type List = Vec<String>;

fn add(list: &mut List, activity: &str) {
     list.push(activity.to_string());
}

fn main() {
     let mut list: List = Vec::new();

     add(&mut list, "Clean the room");
}

String objects implements Deref<Target=str> and therefore &String can be used as &str.

fn main() {
     let mut list: List = Vec::new();

     for item in ["Bread", "Butter", "Tea"] {
         add(&mut list, &format!("Buy {}", item));
     }
}

Output and formatting

Simple output:

fn main() {
     print!("Hello World!");
}

Printing string literals:

fn func(name: &str, place: &str) {
     println!("This is {} from {}.", name, place);
}

fn main() {
     let name = "Joe";
     let place = "Prague";

     func(name, place);
}

Note: This only works because name and place are already references.

More information:

https://doc.rust-lang.org/std/fmt/

Advanced output

You can create allocated strings using string formatting instead of just printing.

use std::io::Write;

fn main() {
     let value = std::f32::consts::PI;
     let text = format!("Pi is {:.2}.\n", value);
     std::io::stdout().write_all(text.as_bytes()).unwrap();
}

Or using a neat shortcut:

use std::io::Write;

fn main() {
     let value = std::f32::consts::PI;
     write!(std::io::stdout(), "Pi is {:.2}.\n", value);
}

All you need is that your values implement the Display trait.

Iteration and debugging

Use a combination of print!() and println!():

fn main() {
     let values = &[1, 2, 3, 4, 5];

     for (idx, value) in values.iter().enumerate() {
         if idx != 0 {
             print!(", ");
         }
         print!("{}", value);
     }
     println!();
}

When convenient, use Debug printing instead:

fn main() {
     let values = &[1, 2, 3, 4, 5];

     println!();
}

This is supported for all types that implement the Debug trait.

User input

Implement Python-like input function:

use std::io::BufRead;
use std::io::{self, Write};

fn input(prompt: &str) -> String {
     print!("{}", prompt);
     io::stdout().flush().unwrap();
     io::stdin().lock().lines().next().unwrap().unwrap()
}

fn main() {
     let name = input("What's your name: ");
     println!("Hello {}!", name);
}

Use input() for interactive command-line programs:

fn main() {
     let a: i32 = input("a = ").parse().unwrap();
     let b: i32 = input("a = ").parse().unwrap();
     let c = a / b;

     println!("{:?} / {} = {}", a, b, c);
}

Mutability of owned values

For owned values mutability only indicates whether we are going to modify the value or not. Passing ownership allows mutability. Note that ownership is not passed for trivial types like integers or arrays.

fn func(mut value: Vec<i32>) {
     value.push(4);
     println!("{:?}", value);
}

fn main() {
     let value = vec![1, 2, 3];
     func(value);
}

Mutability of borrowed values

Rust borrow checker guards concurrent access. Immutable borrow is shared but mutable borrow is exclusive. This provices not only thread safety but also safety in other types of concurrent access.

fn main() {
     let mut values = vec![1, 2, 3];

     for item in &values {
         values.push(10);
     }
     // See compiler error!
}

Library tools like RWLock or RefCell use interior mutability to provide sharable mutable state.

Comments and documentation

Commenting and documenting code:

// Comment

/// Documentation for the following item

//! Documentation for the enclosing item/file

Generate documentation:

cargo doc