* no refund if someone did actually tell you about them already
Normally when using Debug
to print something, the :?
format
operator is used. However, there are other operators that can be
used as well! One useful one is :#?
, which formats using
newlines and indentation to make things more readable.
#[derive(Debug)]
struct Foo {
x: i32,
y: i32,
}
let foo = Foo { x: 1, y: 2 };
println!("Simple debug:\n{:?}", foo);
println!("Pretty debug:\n{:#?}", foo);
This prints out:
Simple debug:
Foo { x: 1, y: 2 }
Pretty debug:
Foo {
x: 1,
y: 2,
}
unimplemented!
Sometimes, you might want to write code using a particular
function without having to write the entire implementation. For
instance, you might want to stub out methods of a struct in
order to write tests for them, or you might want to leave out a
certain feature until later in development. The
unimplemented!
macro expands to an expression that will
compile regardless of the expected type.
enum VerySimpleList<T> {
Empty,
Elem(T, Box<VerySimpleList>),
}
impl<T> VerySimpleList<T> {
fn len(&self) -> usize {
match self {
VerySimpleList::Empty => 0,
VerySimpleList::Elem(..) => unimplemented!(),
}
}
}
Sometimes, you want to create a copy of a struct with one or
more fields different. While you can do this by manually cloning
and mutating the result, there’s an easier way! Using the ..
operator in a struct literal followed by an instance of the
struct will initialize the remaining fields to those of the
instance. Conveniently, this doesn’t rely on Clone
being
implemented on the struct.
#[derive(Debug, Default)]
struct Foo {
x: i32,
y: i32,
}
let a = Foo { x: 1, y: 2 };
let b = Foo { x: 2, ..a };
let c = Foo { x: 2, ..Default::default() };
Sometimes when pattern matching, the cases you want to handle don’t map exactly to the patterns of the data you’re matching on. For instance, you might write some code like this:
fn divide_opt(x: Option<i32>, y: Option<i32>) -> Option<i32> {
match (x, y) {
(Some(i), Some(0)) => None
(Some(i), Some(j)) => Some(i / j)
_ => None,
}
}
Alternately, you can combine two of the cases like this:
fn divide_opt(x: Option<i32>, y: Option<i32>) -> Option<i32> {
match (x, y) {
(Some(i), Some(j)) => {
if j == 0 {
None
} else {
Some(i / j)
}
}
_ => None,
}
}
However, there’s a better way than either of these! Patterns can have a trailing conditional expression condition called a “guard”:
fn divide_opt(x: Option<i32>, y: Option<i32>) -> Option<i32> {
match (x, y) {
(Some(i), Some(j)) if j != 0 => Some(i / j),
_ => None,
}
}
Want to left-pad in Rust? No need for an external package!
Simply use the :>
format operator followed by the length of
the padding, and you’ll get it!
let score1 = 100;
let score2 = 1000;
let score3 = 10000;
println!("{:>5}", score1);
println!("{:>5}", score2);
println!("{:>5}", score3);
This prints out:
100
1000
10000
Oh, did you want to pad things on the right too? No worries;
just use :<
!
let player1 = "first player:";
let player2 = "second player:";
let player3 = "third player:";
println!("{:<14} padded!", player1);
println!("{:<14} padded!", player2);
println!("{:<14} padded!", player3);
This prints out:
first player: padded!
second player: padded!
third player: padded!
But wait, there’s more! If you want to pad on both sides, we’ve got
that too; :^
will do the trick if you tell it how wide the
padded string should be!
let padded = "padded";
println!("[{:^10}]", padded)
This prints out:
[ padded ]
Did someone ask if you can pad with different characters? Sure thing! Stick the character you want to pad with in front of the arrow, and you’re all set!
let title = "SCORES";
let player1 = "first player:";
let player2 = "second player:";
let player3 = "third player:";
let score1 = 100;
let score2 = 1000;
let score3 = 10000;
println!("{:_^20}", title);
println!("{:<14} {:>5}", player1, score1);
println!("{:<14} {:>5}", player2, score2);
println!("{:<14} {:>5}", player3, score3);
This prints out:
_______SCORES_______
first player: 100
second player: 1000
third player: 10000