Posts

#030 Mar 2026

30. dbg! macro

Still using println! for quick debugging? Try dbg! instead — it prints the expression, its value, and the file/line number to stderr. And it returns the value, so you can wrap it around anything.

1
2
3
4
5
6
let a = 2;
let b = dbg!(a * 2) + 1; // prints: [src/main.rs:3] a * 2 = 4
assert_eq!(b, 5);

// works with multiple values too
dbg!(a, b, a + b); // prints each as a tuple

Unlike println!, dbg! takes ownership (or copies for Copy types). If you need to keep the value, pass a reference:

1
2
3
let name = String::from("Ferris");
dbg!(&name); // borrows, doesn't move
println!("{name}"); // still works!

Bonus: dbg! works the same in release builds, and outputs to stderr so it won’t pollute your stdout.

29. Let chains

Tired of deeply nested if let blocks? Rust 2024 edition brings let chains — chain multiple let patterns with && in a single if expression.

1
2
3
4
5
6
7
8
// Before: nested and hard to read
if let Some(a) = opt_a {
    if let Some(b) = opt_b {
        if a > 0 {
            println!("{a} + {b} = {}", a + b);
        }
    }
}

With let chains, flatten the whole thing:

1
2
3
4
5
6
if let Some(a) = opt_a
    && let Some(b) = opt_b
    && a > 0
{
    println!("{a} + {b} = {}", a + b);
}

Works with while too!

1
2
3
4
5
6
7
8
9
let mut iter = vec![Some(1), Some(2), None, Some(4)].into_iter();

while let Some(inner) = iter.next()
    && let Some(val) = inner
{
    println!("got: {val}");
}
// prints: got: 1, got: 2
// stops at None — the inner let fails

You can mix boolean expressions and let bindings freely. Each && can be either a regular condition or a pattern match.

Note: requires edition 2024 (edition = "2024" in Cargo.toml).

#028 Feb 2023

28. Raw string

Curious to know how you can add a double quote in raw strings?

No escape sequences are recognized in raw strings, so adding a backslash does not work.

Use ### to mark the start and the end of a raw string.

1
2
3
4
5
6
7
8
println!("This is a double quote \" ");

// println!(r"This is a double quote " "); // Nope!
// println!(r"This is a double quote \" "); // Nope!

// And this works
println!(r#"This is a double quote " "#);
println!(r###"This is a double quote " "###);
#027 Jan 2023

27. Option's sum/product

Rust’s option implements Sum and Product traits too!

Use it when you want to get None if there is a None element and sum of values otherwise.

1
2
3
let nums: [Option<u32>;3] = [Some(1), Some(10), None];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, None);

or sum of the values …

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, Some(111));

And product.

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().product();
assert_eq!(maybe_nums, Some(1000));
#026 Jan 2023

26. Collecting into Option

Rust’s option implements FromIterator too!

Use it when you want to get None if there is a None element and values otherwise.

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), None];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, None);

or values …

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), Some("Skoda")];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, Some(vec!["Porsche", "Ferrari", "Skoda"]));
#025 Dec 2022

25. Option's iterator

Rust’s option implements an iterator!

1
2
3
4
let o: Option<u32> = Some(200u32);
let mut iter = o.into_iter();
let v = iter.next();
assert_eq!(v, Some(200u32));

Why is this useful? see example.

1
2
3
4
5
6
let include_this_pls = Some(300u32);
let r: Vec<u32> = (0..2).chain(include_this_pls).chain(2..5).collect();
assert_eq!(r, vec![0,1,300,2, 3,4]);
let but_not_this = None;
let r: Vec<u32> = (0..2).chain(but_not_this).chain(2..5).collect();
assert_eq!(r, vec![0,1,2,3,4]);
#024 Dec 2022

24. ..=X

As of 1.66, it is possible to use ..=X in patterns

1
2
3
4
5
6
let result = 20u32;

match result {
    0..=20 => println!("Included"),
    21.. => println!("Sorry"),
}
#023 Dec 2022

23. Enum's default value

Instead of manually implementing Default trait for an enum, you can derive it and explicitly tell which variant should be the default one.

1
2
3
4
5
6
7
8
9
#[derive(Default)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
#022 Nov 2022

22. Enum's Debug

Use Debug trait to print enum values if needed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#[derive(Default,Debug)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
println!("{:?}", car);
#021 Oct 2022

21. Zip longest

Sometimes there is a need to zip two iterables of various lengths.

If it is known which one is longer, then use the following approach:

First one must be longer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let nums1 = [1, 2, 3, 4];
let nums2 = [10, 20];

for (n1, n2) in nums1
    .into_iter()
    .zip(nums2.into_iter().chain(std::iter::repeat(0i32)))
{
    println!("{n1} - {n2}");
}
// Output:
// 1 - 10
// 2 - 20
// 3 - 0
// 4 - 0
#020 Sep 2022

20. let-else statements

As of 1.65, it is possible to use let statement with a refutable pattern.

1
2
3
4
5
6
7
let result: Result<i32, ()> = Ok(20);

let Ok(value) = result else {
  panic!("Heeeelp!!!");
};

assert_eq!(value, 20);
#019 Aug 2022

19. breaking from labeled blocks

As of 1.65, it is possible to label plain block expression and terminate that block early.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let result = 'block: {
    let result = 20i32;
    if result < 10 {
        break 'block 1;
    }
    if result > 10 {
        break 'block 2;
    }
    3
};
assert_eq!(result, 2);
#018 Jul 2022

18. flatten options

Use flatten to iterate over only Some values if you have a collection of Options.

1
2
3
4
5
6
let nums = vec![None, Some(2), None, Some(3), None];

let mut iter = nums.into_iter().flatten();

assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
#016 Jun 2022

16. Option/Result match?!

Try to avoid matching Option or Result.

Use if let instead.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let result = Some(111);

// Not nice
match result {
    Some(x) => println!("{x}"),
    None => {}
};

// Better
if let Some(x) = result {
    println!("{x}");
}
#015 Apr 2022

15. Scan

Iterator adapter similar to fold (see previous bite) - holds some internal state and produces new iterator.

Note that Option is yielded from the closure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let nums = [1, 2, 3];

let mut iter = nums.iter().scan(0, |acc, &x| {
    *acc = *acc + x;
    Some((*acc, x))
});

assert_eq!(iter.next(), Some((1, 1)));
assert_eq!(iter.next(), Some((3, 2)));
assert_eq!(iter.next(), Some((6, 3)));
#014 Apr 2022

14. Find index of item

Use position to find position of an element in iterator. Returns None if the element does not exist.

1
2
3
4
5
6
let nums = [1, 2, 3];

let pos = nums.iter().position(|value| *value == 3);
assert_eq!(pos, Some(2));
let pos = nums.iter().position(|value| *value == 10);
assert_eq!(pos, None);
#017 Mar 2022

17. filter_map

Similar to map but if allows to drop an item.

1
2
3
4
5
6
7
8
let text_nums = ["1", "two", "three", "4"];

let nums: Vec<_> = text_nums
    .iter()
    .filter_map(|x| x.parse::<u32>().ok())
    .collect();

assert_eq!(nums, vec![1, 4]);
#013 Mar 2022

13. Fold

Iterator consumer. Allows the accumulated value to be arbitrary type. Note the different types.

1
2
3
let nums: Vec<u8> = vec![1, 2, 3, 4];
let sum = nums.iter().fold(0u32, |acc, x| acc + *x as u32);
assert_eq!(sum, 10);
#012 Mar 2022

12. Enumerate

Use enumerate to convert iterator over (usize,item) pairs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

for (idx, car) in cars.iter().enumerate() {
    println!("{idx} - {car}");
}

// Output
// 0 - Skoda
// 1 - Ferrari
// 2 - Ford

Useful when index of item is needed.

#011 Mar 2022

11. Fuse

Fuse is an iterator that yields None forever after the underlying iterator yields None once. USage:

1
2
let values = [1,2,3,4];
let iter = values.iter().fuse();

Why is it useful? see example in this bite.

Sometimes an underlying iterator may or may yield Some(T) again after None was returned.

fuse ensures that after a None is returned for the first time, it always returns None.

Example from the rust documentation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Alternate {
    state: i32,
}

impl Iterator for Alternate {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        let val = self.state;
        self.state = self.state + 1;

        if val % 2 == 0 {
            Some(val)
        } else {
            None
        }
    }
}

let mut iter = Alternate { state: 0 };

assert_eq!(iter.next(), Some(0));
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);

let mut iter = iter.fuse();

assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), None);

assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
#010 Feb 2022

10. cloned vs copied

Rust provides built-in methods to copy or clone elements when using an iterator.

Eg.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

What’s the difference? and when is it useful?

You may have done something like this:

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).map(|x| *x).collect();

but instead, use copied.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let nums = vec![1,2,3,4];
let nums2 = vec![5,6,7,8];

// doiing it manually by copying
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| *x).collect();

// doiing it manually by cloneing
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| x.clone()).collect();

// but why not just use provided functionality ??!
let all_nums: Vec<u32> = nums.iter().chain(&nums2).copied().collect();

// or cloned if copy it is not an option
//let all_nums: Vec<u32> = nums.iter().chain(&nums2).cloned().collect();

 assert_eq!(all_nums, vec![1,2,3,4,5,6,7,8]);

Difference between cloned and copied?

Same reasoning aplies as for Copy and Clone traits. Use copied to avoid to accidentally cloing iterator elements. Use copied when possible.

#009 Feb 2022

9. Inspecting iterator

Ever wondered how to print while iterating?

Use inspect.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

let car_lenghts: Vec<u32> = cars
    .iter()
    .enumerate()
    .inspect(|(idx, s)| println!("{idx} - {s}"))
    .map(|(_, name)| name.len() as u32)
    .collect();

assert!( car_lenghts == vec![5,7,4
#008 Feb 2022

8. Drain

Use drain to remove specified range from a vector.

1
2
3
4
5
let mut a = vec![1,2,3,4,5];

let _ = a.drain(0..3);

assert!(a == vec![4,5]);

What is the difference to call into_iter which returns T ?

into_iter takes the collection by value and consumes it.

drain borrows mutable reference, and returns a drain iterator of elements. If such iterator is dropped without exhausting it, all elements are dropped.

#007 Feb 2022

7. iter() vs into_iter()

What is the difference between .iter() and .into_iter()?

iter yields &T

into_iter may yield any of T, &T or &mut T based on context.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.iter() {
    println!("{car}");
}

for car in cars.into_iter() {
    println!("{car}");
}

this works but …

This does not. This results in compile error because cars are moved due to into_iter call.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.into_iter() {
  println!("{car}");
}

for car in cars.iter() {
    println!("{car}");
}
#006 Jan 2022

6. non-exhaustive enums

Use non-exhaustive attribute to indicate that enum may have more variants in future.

1
2
3
4
5
#[non_exhaustive]
enum Event{
  Error,
  Completed(String)
}

If such enum is used in match pattern, it is required to handle ’_’ pattern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let event = Event::Error;
match event {
    Event::Error => {
        println!("error")
    }
    Event::Completed(s) => {
        println!("completed {}", s)
    }
    _ => {
        println!("unknown")
    }
}

How is this useful?

If you are a maintainer of a library and you’d expect to add more variants in the future.

This approach helps you to build a library with non-breaking code changes for library users.

#005 Jan 2022

5. Loop breaks and labels

Breaks and labels in loops?

IT is possible to use a label to specify which enclosing loop is affected.

1
2
3
4
5
6
7
8
let s = 'outer: loop {
      for idx in 0..10 {
          if idx == 4 {
              break 'outer idx;
          }
      }
  };
assert!(s == 4);
#004 Jan 2022

4. Option -> Result

Convert Option to Result easily.

1
2
3
let o: Option<u32> = Some(200u32);
let r: Result<u32,()> = o.ok_or(());
assert_eq!(r, Ok(200u32));
#003 Jan 2022

3. Result -> Option

Convert Result to Option easily.

1
2
3
let r: Result<u32,()> = Ok(200u32);
let o: Option<u32> = r.ok();
assert_eq!(o, Some(200u32));
#002 Jan 2022

2. mysterious @

Use @ to bind a pattern to a name.

1
2
3
4
5
6
let foo = 4;

match foo {
    x @ 0..=5 => assert_eq!(x,4),
    y @ _ => panic!("{}-too many!", y)
}
1
2
3
4
5
6
let point: (u32,u32) = (1, 2);

match point {
    (1, y @ _) => assert_eq!(y,2),
    _ => {}
}
1
2
3
4
5
let point: (u32,u32) = (1, 2);
let mypoint @ (x, _) = point;

assert_eq!(x,1);
assert_eq(mypoint,point);
#001 Jan 2022

1. matches!

Need to check if an expression matches certain pattern?

1
2
let foo = vec!['1','2'];
assert!(matches!(foo.len(), 0..=1));

The example above checks if len is 0 or 1.

Or

1
2
3
let bar = Some(4);

assert!(matches!(bar, Some(x) if x > 2));
#000 Jan 2022

0. Hello, rustbites!

Hello and welcome to rustbites.com

1
2
let hello = "Hello, rustbites!";
println!("{}", hello);

explanation and details