1 unstable release
0.1.0 | Sep 7, 2023 |
---|
#2615 in Rust patterns
16KB
153 lines
Rust library for diplaying Option
s.
Usage
use show_option::prelude::*;
println!("received bytes: {}", None::<usize>.show_or("none")); // "received bytes: none"
println!("amount: {}", Some(20).show_prefixed_or("$", "-")); // "amount: $20"
println!("amount: {}", format_option!(Some(20), "${}", "-")); // "amount: $20"
lib.rs
:
Extension methods and convenient macro for formatting Option
s without
unnecessary code duplication and allocations.
Usage
// Import all
use show_option::prelude::*;
// OR, if you prefer, import explicitly only what you need:
use show_option::ShowOption as _; // Extension methods for `Option`.
use show_option::format_option; // Macro
// Display 'Option's
println!("received bytes: {}", None::<usize>.show_or("none")); // "received bytes: none"
println!("amount: {}", Some(20).show_prefixed_or("$", "-")); // "amount: $20"
println!("amount: {}", format_option!(Some(20), "${}", "-")); // "amount: $20"
See full list of methods in ShowOption
documentation.
Also see format_option
macro.
If there are many places you want to apply same formatting, consider creating function:
fn format_amount(amount: &Option<usize>) -> impl Display + '_ {
amount.show_prefixed_or("$", "-")
}
println!("amount: {}", format_amount(&Some(20))); // prints "amount: $20"
Sometimes even better solution is to create new wrapper type with own Display
implementation.
Motivation
Option
type doesn't implement Display
intentionally, because it's formatting
is context-dependant. Should None
be formatted as empty string, "none"
,
or "missing"
?
One way to handle it is conditional formatting:
let bytes = Some(20);
match bytes {
Some(value) => println!("bytes received: {}", value),
None => println!("bytes received: none"),
}
it is very flexible, but it leads to format string duplication.
Another way is to convert optional value to it's representation before formatting. It works in simple cases, when default value has the same type as inner value:
let bytes = Some(20);
println!("bytes received: {}", bytes.unwrap_or(0));
But when types are different, it leads to verbose code and unnecessary string allocations:
let bytes = Some(20);
println!("bytes received: {}", bytes.map_or("none".to_string(), |value| value.to_string()));
// ↑ allocation ↑ allocation
It is possible to avoid allocations by casting both branches to Display
trait reference,
but it's very verbose:
use std::fmt::Display;
let bytes = Some(20);
println!("bytes received: {}", bytes.as_ref().map_or(&"none" as &dyn Display, |value| value as &dyn Display));
This crate basically wraps last solution into nice methods.
Dependencies
~5KB