101 Chunks of Rust
I’m learning writing more & more rust. But I really lack writing idiomatic code. So, I’ve decided to gather small useful Rust snippets on this page. No particular order, the more I want to write, the more it will be written on it.
Goal: One snippet, one crate/doc page/poc/whatever.
This page was last updated on 2022-01-03 and it is still WIP!
Have fun!
The Snippets
Navigating into the command line args
use std::env;
fn main() {
println!("There is {} env.", env::args().len());
for arg in env::args() {
println!("- {}", arg);
}
}
Also, you can check the clap crate.
Filtering numbers
use std::env;
use std::str::FromStr;
fn main() {
let numbers = env::args()
.skip(1)
.map(|arg| u64::from_str(&arg))
.filter(|arg| arg.is_ok())
.map(|arg| arg.unwrap())
.collect::<Vec<u64>>();
println!("{:?}", numbers);
}
The std::str::FromStr trait documentation.
Pretty logging
pretty_env_logger
is a simple logger built on top of log
and that is allowing to write logs using colors.
extern crate pretty_env_logger;
#[macro_use] extern crate log;
fn main() {
pretty_env_logger::init_timed();
info!("such information");
warn!("o_O");
error!("much error");
}
Don’t forget to use log & pretty_env_logger crates to enable this.
Returning values from a loop
fn main() {
let mut count = 0;
let result = loop {
if count == 10 {
break count * 10;
}
count += 1;
};
println!("result: {:?}", result);
}
Simple password generator
// Don't forget to add the rand crate in Cargo.toml file:
//
// [dependencies]
// rand = "0.8.4"
//
use rand::prelude::*;
fn generate(size: usize) -> String {
let mut rng = rand::thread_rng();
let printable_chars = (0x20u8..=0x7e).map(|c| c as char).collect::<Vec<char>>();
(0..size)
.map(|_| rng.gen::<usize>() % printable_chars.len())
.map(|idx| printable_chars[idx])
.collect()
}
fn main() {
for _ in 0..32 {
println!("{}", generate(16));
}
}
It makes use of the rand crate.
Swapping variables
The following snippet will swap 2 variables (which type has Copy
trait), and will print their address.
fn swap<T: Copy>(a: &mut T, b: &mut T) {
let z = *a;
*a = *b;
*b = z;
}
fn main() {
let mut a = 1;
let mut b = 2;
println!("ptr: {:p}: val: {} // ptr: {:p}; val: {}", &a, a, &b, b);
swap(&mut a, &mut b);
println!("ptr: {:p}: val: {} // ptr: {:p}; val: {}", &a, a, &b, b);
}
The Itertools crate
use itertools::Itertools;
use itertools::{merge,partition,zip};
fn main() {
// Get all permutations
println!("{:?}", (0..5).permutations(5).collect_vec());
// Get all uniques
println!("{:?}", vec![1, 2, 3, 2, 1].iter().unique().collect_vec());
// combine elements from first array with second's
println!("{:?}", vec![0, 1, 2].iter().zip(vec![4, 5, 6]).collect_vec());
println!("{:?}", zip(vec![0, 1, 2], vec![0, 1, 2]).collect_vec());
// partition elements of array into 2.
let mut l = vec![1, 2, 3, 4, 5, 6];
partition(&mut l, |z| { z % 2 == 0 });
println!("{:?}", l); // [6, 2, 4, 3, 5, 1]
// merge two array into an iterator
println!("{:?}", merge(&[0, 1, 2], &[4, 5, 6]).collect_vec()); // [0, 1, 2, 4, 5, 6]
println!("{:?}", merge(&[0, 1, 2], &[0, 1, 2]).collect_vec()); // [0, 0, 1, 1, 2, 2]
}
Check the itertools functions & itertools trait.
Implementing Display & FromStr traits
use std::str::FromStr;
use std::num::ParseIntError;
use std::fmt;
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "P <{},{}>", self.x, self.y)
}
}
impl FromStr for Point {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts = s.split(",")
.collect::<Vec<&str>>()
.iter().map(|&x| x.parse::<i32>().unwrap())
.collect::<Vec<i32>>();
Ok(Self {
x: parts[0],
y: parts[1],
})
}
}
fn main() {
let p: Point = Point::from_str("-1,-1").unwrap();
println!("{}", p);
}
As seen as on Rust By Example (Display of official doc); FromStr.
Testing out Iterator traits
use rand::Rng;
#[derive(Clone, Copy, Debug)]
struct Random {
}
impl Iterator for Random {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
let n = rand::thread_rng().gen::<u32>();
if n % 100 == 0 {
None
} else {
Some(n)
}
}
}
fn main() {
let random_numbers = Random{};
for v in random_numbers {
println!("- {}", v);
}
println!("There is {} elements in this run.", random_numbers.count());
}
The Iterator trait documentation. Rust By Example’s.
Generic structures & enums
struct Testaroo<T> {
val0: T,
}
enum MyOption<T> {
None,
Some(Testaroo<T>)
}
fn main() {
// All the following code is valid and can be used as it!
let _ = Testaroo{ val0: "a" };
let _ : Testaroo<u64> = Testaroo{ val0: 42 };
let _ = Testaroo{ val0: 0.0 };
let _ : MyOption<bool> = MyOption::None;
let _ = MyOption::Some(Testaroo{ val0: true });
}
Arrays, vec & slices
fn main() {
let array : [u32; 4] = [1, 2, 3, 4];
let array_vec = array.to_vec();
let slice0 = &array[2..=3];
let slice1 = &array_vec[1..=2];
// Outputs: array:[1, 2, 3, 4] vec:[1, 2, 3, 4] slice:[3, 4] slice:[2, 3]
println!("array:{:?} vec:{:?} slice:{:?} slice:{:?}",
array, array_vec, slice0, slice1);
// auto-filled array declaration:
let vbool = [false; 100];
let vec: Vec<u32> = Vec::with_capacity(256);
println!("len:{} capacity:{}", vec.len(), vec.capacity());
let s0: &[bool] = &vbool;
println!("len:{}", s0.len());
let print = |n: &[bool]| {
for el in n {
println!("{:?}", el);
}
};
print(s0); // This will write a lot of "false".
print(&s0[..42]); // First 42 elements
print(&s0[99..]); // last element
}
More about arrays, slices & Vec.
String to bytes array to &str
fn main() {
let s: &str = "新年快乐";
println!("{}", s);
let s: String = s.to_string(); // this copies the string
println!("len: {} chars: {}", s.len(), s.chars().count());
let s: &[u8] = &s.as_bytes(); // convert to bytes
println!("{}", String::from_utf8(s.to_vec()).unwrap()); // output: 新年快乐
let elements = ["a", "b", "c", "d"];
println!("{}", elements.concat()); // output: abcd
println!("{}", elements.join(", ")); // output: a, b, c, d
println!("{}", elements.concat().to_uppercase()); // output: ABCD
println!("{}", elements.join(", ").replace(",", ";")); // output: "A; B; C; D"
println!("{}", String::from("\n A \n").trim()); // output: "A"
}
The Uuid crate
// Don't forget to add in Cargo.toml:
// uuid = { version = "0.8.2", features = ["v4", "v5"] }
use uuid::Uuid;
fn main() {
let uuid = Uuid::new_v4();
println!("{:?}", uuid.to_urn());
// Doing some conversion
println!("{:?}", uuid.as_bytes());
let uuid = Uuid::from_bytes(*uuid.as_bytes());
println!("{:?}", uuid.to_urn());
let uuid = Uuid::new_v5(
&Uuid::NAMESPACE_URL,
"example.org".as_bytes(),
);
println!("{:?}", uuid.to_urn());
}
Anyhow
Sometimes, handling errors in rust can be boring due to the type the error can be. There is a set of different crates to make this easier, and anyhow
is one of them:
use anyhow::Result;
fn main() -> Result<()> {
let contents = std::fs::read_to_string("test")?;
println!("{:?}", contents);
Ok(())
}
All the crates
- anyhow: Flexible error type;
- clap: Command line argument parser;
- itertools: Extra iterator adaptors, functions and macros;é
- pretty_env_logger: Simple logger built on top of env_logger, configurable via environment variables & writing nice colored messages depending their log levels;
- rand: Random number generation, with quite some useful features.
- uuid: Generate and parse UUIDs.