How to achieve conditional nested formatting without multiple allocations? - rust

I have a format string consisting of multiple conditional components and I'm looking for a solution that doesn't need multiple allocations of Strings for the intermediate steps. If I create each single component of the final format string with the format!-macro then it works but I need an allocation for each component.
I tried experimenting with using only macros to generate the complex format string and its arguments. However, this always resulted in "temporary value is freed at the end of this statement" errors. I tried to use one single buffer of type impl core::fmt::Write but I couldn't make success with this either.
On a high level, I want something like this:
fn main() {
let prefix_include_a = true;
let prefix_include_b = true;
// prefix itself is a formatted string and it is further formatted here
println!("{prefix:<10}{message:>10}!",
prefix = format_prefix(prefix_include_a, prefix_include_b),
message = "message"
);
}
// formats the prefix component of the final string.
// needs multiple String allocations as `format!` is used
fn format_prefix(inc_a: bool, inc_b: bool) -> String {
format!("[{a:<5}{b:<5}]",
a = if inc_a {
format!("{:.1}", 1.234)
} else {
format!("")
},
b = if inc_b {
format!("{:.2}", 1.234)
} else {
format!("")
},
)
}
Is this possible with no or only one single allocation?

The simplest solution is to just write! directly to the underlying stream e.g.
use std::io::{stdout, Write};
fn main() {
let prefix_include_a = true;
let prefix_include_b = true;
let mut stdout = stdout();
let _ = format_prefix(&mut stdout, prefix_include_a, prefix_include_b);
let _ = write!(stdout, "{:>10}", "message");
}
// formats the prefix component of the final string.
// needs multiple String allocations as `format!` is used
fn format_prefix(mut s: impl Write, inc_a: bool, inc_b: bool) -> std::io::Result<()> {
write!(s, "[")?;
if inc_a {
write!(s, "{:<5.1}", 1.234)?;
} else {
write!(s, " ")?;
}
if inc_b {
write!(s, "{:<5.2}", 1.234)?;
} else {
write!(s, " ")?;
}
write!(s, "]")?;
Ok(())
}
An alternative is to reify prefix into a type, and implement Display for it. I would think (hope?) the formatter is a passthrough to the underlying stream, though I've never actually looked:
use std::io::{stdout, Write};
fn main() {
let prefix_include_a = true;
let prefix_include_b = true;
println!(
"{prefix:<10}{message:>10}!",
prefix = Prefix(prefix_include_a, prefix_include_b),
message = "message"
);
}
struct Prefix(bool, bool);
impl std::fmt::Display for Prefix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
if self.0 {
write!(f, "{:<5.1}", 1.234)?;
} else {
write!(f, " ")?;
}
if self.1 {
write!(f, "{:<5.2}", 1.234)?;
} else {
write!(f, " ")?;
}
write!(f, "]")?;
Ok(())
}
}
Note: I've not handled the padding of the prefix in either version, though I don't think it makes much sense: both prefix values are padded to 5, so the prefix is always at least 12 wide. Padding to 10 makes no sense.
The prefix object could, however, use the externally specified padding to distribute to internal paddings, if that's desirable. See std::fmt::Formatter for information you can obtain about formatting specifiers.
To clean up the conditionals, you could probably use format_args!, though I've pretty little experience with that.

Related

How can I easily get a reference to a value after it has been moved into a tuple-type enum variant?

I want to move a value into a tuple-type enum variant and obtain a reference to the value after it has been moved. I see how this is possible with an if let statement, but this seems like this should be unnecessary when the particular variant is known statically.
Is there any way to get the reference to the moved value without requiring an if let or match?
This code block is a simple illustration of my question (see below for a more challenging case):
enum Transport {
Car(u32), // horsepower
Horse(String), // name
}
fn do_something(x: &String) {
println!(x);
}
fn main() {
// Can I avoid needing this if, which is clearly redundant?
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
else {
// Can never happen
}
// I tried the following, it gives:
// "error[E0005]: refutable pattern in local binding: `Car(_)` not covered"
let Transport::Horse(ref name) = Transport::Horse("daisy".into());
}
It is easy to find ways to side-step the issue in the above code, since there are no real interface requirements. Consider instead the following example, where I am building a simple API for building trees (where each node can have n children). Nodes have an add_child_node method returning a reference to the node that was added, to allow chaining of calls to quickly build deep trees. (It is debatable whether this is a good API, but that is irrelevant to the question). add_child_node must return a mutable reference to the contents of an enum variant. Is the if let required in this example (without changing the API)?
struct Node {
children: Vec<Child>,
// ...
}
enum Child {
Node(Node),
Leaf
}
impl Node {
fn add_child_node(&mut self, node: Node) -> &mut Node {
self.children.push(Child::Node(node));
// It seems like this if should be unnecessary
if let Some(&mut Child::Node(ref mut x)) = self.children.last() {
return x;
}
// Required to compile, since we must return something
unreachable!();
}
fn add_child_leaf(&mut self) {
// ...
}
}
No. You can use unreachable!() for the else case, and it's usually clear even without message/comment what's going on. The compiler is also very likely to optimize the check away.
If the variants have the same type you can implement AsRef and use the Transport as a &str:
enum Transport {
Car(String),
Horse(String),
}
fn do_something<S: AsRef<str>>(x: &S) {
println!("{}", x.as_ref());
}
impl AsRef<str> for Transport {
fn as_ref(&self) -> &str {
match self {
Transport::Car(s) => s,
Transport::Horse(s) => s,
}
}
}
fn main() {
let transport = Transport::Horse("daisy".into());
do_something(&transport)
}
Playground
Otherwise you need to use a let if binding as you are doing. No need to use an else clause if you don't want to:
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
define From<Transport> for String:
…
impl From<Transport> for String {
fn from(t: Transport) -> String {
match t {
Transport::Car(value) => value.to_string(),
Transport::Horse(name) => name,
}
}
}
fn do_something(x: Transport) {
println!("{}", String::from(x));
}
fn main() {
let horse = Transport::Horse("daisy".to_string());
let car = Transport::Car(150);
do_something(horse);
do_something(car);
}

How to convert a vector of enums into a vector of inner values of a specific variant of that enum

The following code example is the best that I have come up with so far:
enum Variant {
VariantA(u64),
VariantB(f64),
}
fn main() {
let my_vec = vec![Variant::VariantA(1),
Variant::VariantB(-2.0),
Variant::VariantA(4),
Variant::VariantA(3),
Variant::VariantA(2),
Variant::VariantB(1.0)];
let my_u64_vec = my_vec
.into_iter()
.filter_map(|el| match el {
Variant::VariantA(inner) => Some(inner),
_ => None,
})
.collect::<Vec<u64>>();
println!("my_u64_vec = {:?}", my_u64_vec);
}
I would like to know if there is a less verbose way of obtaining the vector of inner values (i.e., Vec<u64> in the example). It feels like I might be able to use something like try_from or try_into to make this less verbose, but I cannot quite get there.
Enums are not "special" and don't have much if any implicitly associated magic, so by default yes you need a full match -- or at least an if let e.g.
if let Variant::VariantA(inner) = el { Some(inner) } else { None }
However nothing prevents you from implementing whatever utility methods you're thinking of on your enum e.g. get_a which would return an Option<A> (similar to Result::ok and Result::err), or indeed to implement TryFrom on it:
use std::convert::{TryFrom, TryInto};
enum Variant {
VariantA(u64),
VariantB(f64),
}
impl TryFrom<Variant> for u64 {
type Error = ();
fn try_from(value: Variant) -> Result<Self, Self::Error> {
if let Variant::VariantA(v) = value { Ok(v) } else { Err(()) }
}
}
fn main() {
let my_vec = vec![Variant::VariantA(1),
Variant::VariantB(-2.0),
Variant::VariantA(4),
Variant::VariantA(3),
Variant::VariantA(2),
Variant::VariantB(1.0)];
let my_u64_vec = my_vec
.into_iter()
.filter_map(|el| el.try_into().ok())
.collect::<Vec<u64>>();
println!("my_u64_vec = {:?}", my_u64_vec);
}

How do I make a Range reverse on condition?

I have a range, that I want to reverse if a condition is satisfied. Since for i in 0..9 will iterate the same way as for i in 9..0 simply swapping out the numbers will not work. Also (0..9).stepy_by(-1) is not an option as .step_by() only accepts a usize. Therefore I tried so implement something like the following:
fn create_range(rev: bool) -> Range<usize> {
if rev {
0..9
} else {
(0..9).rev()
}
}
Which unfortunately also des not work since 0..9 returns a Range<usize> but (0..9).rev() returns a Rev<Range<usize>> so the types do not match.
I ended up putting everything that I call within the loop in a function but I am not really satisfied with that.
if rev {
for i in (0..9).rev() {
do_stuff(i);
}
} else {
for i in 0..9 {
do_stuff(i);
}
}
The question is: Would it be simply possible to reverse a range if a condition is satisfied?
itertools has the type Either that can be used to return either of two compatible iterators:
use itertools; // 0.8.2
fn create_range(
rev: bool,
) -> itertools::Either<impl Iterator<Item = usize>, impl Iterator<Item = usize>> {
if !rev {
itertools::Either::Left(0..9)
} else {
itertools::Either::Right((0..9).rev())
}
}
fn main() {
println!("Not reversed:");
for a in create_range(false) {
println!("{}", a);
}
println!("Reversed:");
for a in create_range(true) {
println!("{}", a);
}
}
(Permalink to the playground)
Another solution without itertools:
fn create_range(rev: bool) -> impl Iterator<Item = usize> {
let (mut start, step) = if rev {
(9usize, usize::max_value())
} else {
(usize::max_value(), 1)
};
std::iter::repeat_with(move || {
start = start.wrapping_add(step);
start
})
.take(9)
}
fn main() {
println!("Not reversed:");
for a in create_range(false) {
println!("{}", a);
}
println!("Reversed:");
for a in create_range(true) {
println!("{}", a);
}
}
(Permalink to the playground)
This is a bit more complicated and won't generalize as easily to things that are not ranges. It might be a tiny bit faster as well. The dance with usize::max_value() and wrapping_add is necessary to avoid overflow checks, but is perfectly defined.
If you don't care about allocation and performance isn't a huge concern, you can do something like:
let vals = if start < end {
(start..end).collect::<Vec<_>>()
} else {
(end..start).rev().collect::<Vec<_>>()
};
for i in vals {
...
}
Obviously this isn't ideal since you're allocating all those numbers, but it's a quick/dirty solution that might work in some cases.

Consolidating multiple copies of a character at start of string into one in Rust

I'm working on a parser for a mini language, and I have the need to differentiate between plain strings ("hello") and strings that are meant to be operators/commands, and start with a specific sigil character (e.g. "$add").
I also want to add a way for the user to escape the sigil, in which a double-sigil gets consolidated into one, and then is treated like a plain string.
As an example:
"hello" becomes Str("hello")
"$add" becomes Operator(Op::Add)
"$$add" becomes Str("$add")
What would be the best way to do this check and manipulation? I was looking for a method that counts how many times a character appears at the start of a string, to no avail.
Can't you just use starts_with?
fn main() {
let line_list= [ "hello", "$add", "$$add" ];
let mut result;
for line in line_list.iter() {
if line.starts_with("$$") {
result = line[1..].to_string();
}
else if line.starts_with("$") {
result = format!("operator:{}", &line[1..]);
}
else {
result = line.to_string();
}
println!("result = {}", result);
}
}
Output
result = hello
result = operator:add
result = $add
According to the comments, your problem seems to be related to the access to the first chars.
The proper and efficient way is to get a char iterator:
#[derive(Debug)]
enum Token {
Str(String),
Operator(String),
}
impl From<&str> for Token {
fn from(s: &str) -> Self {
let mut chars = s.chars();
let first_char = chars.next();
let second_char = chars.next();
match (first_char, second_char) {
(Some('$'), Some('$')) => {
Token::Str(format!("${}", chars.as_str()))
}
(Some('$'), Some(c)) => {
// your real handling here is probably different
Token::Operator(format!("{}{}", c, chars.as_str()))
}
_ => {
Token::Str(s.to_string())
}
}
}
}
fn main() {
println!("{:?}", Token::from("π"));
println!("{:?}", Token::from("hello"));
println!("{:?}", Token::from("$add"));
println!("{:?}", Token::from("$$add"));
}
Result:
Str("π")
Str("hello")
Operator("add")
Str("$add")
playground

Reading Bytes From a Reader

I'm writing something to process stdin in blocks of bytes, but can't seem to work out a simple way to do it (though I suspect there is one).
fn run() -> int {
// Doesn't compile: types differ
let mut buffer = [0, ..100];
loop {
let block = match stdio::stdin().read(buffer) {
Ok(bytes_read) => buffer.slice_to(bytes_read),
// This captures the Err from the end of the file,
// but also actual errors while reading from stdin.
Err(message) => return 0
};
process(block).unwrap();
}
}
fn process(block: &[u8]) -> Result<(), IoError> {
// do things
}
My questions:
What's the "standard" way to do this? (I've been trying/hoping to use and_then()/or_else())
How can I differentiate between the Err(IoError) from end of the file, and the Err that's actually an error?
The previously accepted answer was outdated (Rust v1.0). EOF is no longer considered an error. You can do it like this:
use std::io::{self, Read};
fn main() {
let mut buffer = [0; 100];
while let Ok(bytes_read) = io::stdin().read(&mut buffer) {
if bytes_read == 0 { break; }
process(&buffer[..bytes_read]).unwrap();
}
}
fn process(block: &[u8]) -> Result<(), io::Error> {
Ok(()) // do things
}
Note that this may not result in the expected behavior: read doesn't have to fill the buffer, but may return with any number of bytes read. In the case of stdin the read implementation returns every time a newline is detected (pressing enter in terminal).
Rust API documentation states that:
Note that end-of-file is considered an error, and can be inspected for
in the error's kind field.
The IoError struct looks like this:
pub struct IoError {
pub kind: IoErrorKind,
pub desc: &'static str,
pub detail: Option<String>,
}
The list is all kinds is at http://doc.rust-lang.org/std/io/enum.IoErrorKind.html
You can match it like this:
match stdio::stdin().read(buffer) {
Ok(_) => println!("ok"),
Err(io::IoError{kind:io::EndOfFile, ..}) => println!("end of file"),
_ => println!("error")
}

Resources