Lifetime of format!() in match expression is too short - rust

I am implementing a custom Display::fmt for my own struct which represent an interval.
struct Range<T> {
lower: Option<T>,
upper: Option<T>,
}
A range could be Range { lower: Some(1), upper: None }, which means it contains all integers from 1 up to infinity (or the limit of i32 I suppose).
I want to implement Display::fmt to use T's Display::fmt if the bound is not None and to display an empty string otherwise:
let range = Range { lower: Some(1), upper: None }
println!("{}", range); // Prints <1,>
let range = Range { lower: Some(1), upper: Some(10) }
println!("{}", range); // Prints <1,10>
let range = Range { lower: None, upper: Some(10) }
println!("{}", range); // Prints <,10>
I have started my implementation but have trouble with the match expression and the lifetime of the string produced by format!(). The problem with my implementation is that the string returned by format doesn't live long enough to be used further down.
fn main() {
let opt = Some(1);
let opt_display = match opt {
Some(x) => &format!("{}", x), // error: borrowed value does not live long enough
None => "",
};
println!("opt: {}", opt_display);
}
Why doesn't my approach work and what is a good solution to my problem?

I'm no expert on lifetimes, but I believe the problem here is that you are trying to return a &String from a String created by format! inside the match. Since the scope of the format is only inside the scope, the borrow checker complains.
To fix this you can use an owned string.
fn main() {
let opt = Some(1);
let opt_display = match opt {
Some(ref x) => format!("{}", x), // Allowed since opt_display now owns the string
None => "".into(),
};
// Another way to achieve the same thing.
//let opt_display = opt.map(|s| format!("{}", s)).unwrap_or("".into());
println!("opt: {}", opt_display);
}

When implementing Display, there's no need to return strings; you can just write!() into the provided formatter.
It would look like:
impl<T: Display> Display for Range<T> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(fmt, "<")?;
if let Some(v) = self.lower {
write!(fmt, "{}", v)?;
}
write!(fmt, ",")?;
if let Some(v) = self.upper {
write!(fmt, "{}", v)?;
}
write!(fmt, ">")
}
}
Playground

As Emilgardis has already explained, you are trying to return a reference to a value that will be dropped while the reference is still around. Congratulations, you just tried to create memory unsafety that would have caused a crash (or worse) in C or C++, but Rust prevented it!
One efficiency improvement you can make is to only allocate in one case:
fn main() {
let opt = Some(1);
let opt_display = opt.map(|s| format!("{}", s));
// Type not needed, only used to assert the type is what we expect
let opt_display_str: &str = opt_display.as_ref().map(String::as_str).unwrap_or("");
println!("opt: {}", opt_display_str);
}
You could also use a Cow, which allows either an owned or borrowed string. Note how similar it is to the other answer, but this doesn't allocate in the case of None:
use std::borrow::Cow;
fn main() {
let opt = Some(1);
let opt_display: Cow<str> = match opt {
Some(ref x) => format!("{}", x).into(),
None => "".into(),
};
println!("opt: {}", opt_display);
}
I want to implement Display::fmt
The best thing to do is probably to avoid any allocation then. You will be handed a formatter that you write! to, just call write! in each match arm. This might introduce a bit of duplication, but is probably more efficient. Without the formatter, it would look something like:
fn main() {
let opt = Some(1);
print!("opt: ");
if let Some(ref x) = opt {
print!("{}", x);
}
println!("");
}
Substitute write!(f, for print!( inside the formatter and return on error.

Related

`while let Ok(t) ... = try_read!(...)` to make neater reading loop

Is it possible to make short, neat loop that will call , as long as result is Ok(x) and act on x ?
E.g. sth like :
use text_io::try_read; // Cargo.toml += text_io = "0.1"
fn main() {
while let Ok(t): Result<i64, _> = try_read!() {
println!("{}", t);
}
}
fails to compile.
If I try to provide type info, then it fails,
when I don't provide , then obviously it's ambiguous how to resolve try_read!.
Here is working - but IMHO way longer - snippet:
use text_io::try_read; // Cargo.toml += text_io = "0.1"
fn main() {
loop {
let mut tok: Result<i64, _> = try_read!();
match tok {
Ok(t) => println!("{}", t),
Err(_) => break,
}
}
}
You can qualify Ok as Result::Ok and then use the "turbofish" operator to provide the concrete type:
fn main() {
while let Result::<i64, _>::Ok(t) = try_read!() {
println!("{}", t);
}
}
(while let Ok::<i64, _>(t) also works, but is perhaps a bit more cryptic.)
Another option is to request the type inside the loop - rustc is smart enough to infer the type for try_read!() from that:
fn main() {
while let Ok(t) = try_read!() {
let t: i64 = t;
println!("{}", t);
}
}
The latter variant is particularly useful in for loops where the pattern match is partly hidden, so there is no place to ascribe the type to.

How to pass &mut str and change the original mut str without a return?

I'm learning Rust from the Book and I was tackling the exercises at the end of chapter 8, but I'm hitting a wall with the one about converting words into Pig Latin. I wanted to see specifically if I could pass a &mut String to a function that takes a &mut str (to also accept slices) and modify the referenced string inside it so the changes are reflected back outside without the need of a return, like in C with a char **.
I'm not quite sure if I'm just messing up the syntax or if it's more complicated than it sounds due to Rust's strict rules, which I have yet to fully grasp. For the lifetime errors inside to_pig_latin() I remember reading something that explained how to properly handle the situation but right now I can't find it, so if you could also point it out for me it would be very appreciated.
Also what do you think of the way I handled the chars and indexing inside strings?
use std::io::{self, Write};
fn main() {
let v = vec![
String::from("kaka"),
String::from("Apple"),
String::from("everett"),
String::from("Robin"),
];
for s in &v {
// cannot borrow `s` as mutable, as it is not declared as mutable
// cannot borrow data in a `&` reference as mutable
to_pig_latin(&mut s);
}
for (i, s) in v.iter().enumerate() {
print!("{}", s);
if i < v.len() - 1 {
print!(", ");
}
}
io::stdout().flush().unwrap();
}
fn to_pig_latin(mut s: &mut str) {
let first = s.chars().nth(0).unwrap();
let mut pig;
if "aeiouAEIOU".contains(first) {
pig = format!("{}-{}", s, "hay");
s = &mut pig[..]; // `pig` does not live long enough
} else {
let mut word = String::new();
for (i, c) in s.char_indices() {
if i != 0 {
word.push(c);
}
}
pig = format!("{}-{}{}", word, first.to_lowercase(), "ay");
s = &mut pig[..]; // `pig` does not live long enough
}
}
Edit: here's the fixed code with the suggestions from below.
fn main() {
// added mut
let mut v = vec![
String::from("kaka"),
String::from("Apple"),
String::from("everett"),
String::from("Robin"),
];
// added mut
for mut s in &mut v {
to_pig_latin(&mut s);
}
for (i, s) in v.iter().enumerate() {
print!("{}", s);
if i < v.len() - 1 {
print!(", ");
}
}
println!();
}
// converted into &mut String
fn to_pig_latin(s: &mut String) {
let first = s.chars().nth(0).unwrap();
if "aeiouAEIOU".contains(first) {
s.push_str("-hay");
} else {
// added code to make the new first letter uppercase
let second = s.chars().nth(1).unwrap();
*s = format!(
"{}{}-{}ay",
second.to_uppercase(),
// the slice starts at the third char of the string, as if &s[2..]
&s[first.len_utf8() * 2..],
first.to_lowercase()
);
}
}
I'm not quite sure if I'm just messing up the syntax or if it's more complicated than it sounds due to Rust's strict rules, which I have yet to fully grasp. For the lifetime errors inside to_pig_latin() I remember reading something that explained how to properly handle the situation but right now I can't find it, so if you could also point it out for me it would be very appreciated.
What you're trying to do can't work: with a mutable reference you can update the referee in-place, but this is extremely limited here:
a &mut str can't change length or anything of that matter
a &mut str is still just a reference, the memory has to live somewhere, here you're creating new Strings inside your function then trying to use these as the new backing buffers for the reference, which as the compiler tells you doesn't work: the String will be deallocated at the end of the function
What you could do is take an &mut String, that lets you modify the owned string itself in-place, which is much more flexible. And, in fact, corresponds exactly to your request: an &mut str corresponds to a char*, it's a pointer to a place in memory.
A String is also a pointer, so an &mut String is a double-pointer to a zone in memory.
So something like this:
fn to_pig_latin(s: &mut String) {
let first = s.chars().nth(0).unwrap();
if "aeiouAEIOU".contains(first) {
*s = format!("{}-{}", s, "hay");
} else {
let mut word = String::new();
for (i, c) in s.char_indices() {
if i != 0 {
word.push(c);
}
}
*s = format!("{}-{}{}", word, first.to_lowercase(), "ay");
}
}
You can also likely avoid some of the complete string allocations by using somewhat finer methods e.g.
fn to_pig_latin(s: &mut String) {
let first = s.chars().nth(0).unwrap();
if "aeiouAEIOU".contains(first) {
s.push_str("-hay")
} else {
s.replace_range(first.len_utf8().., "");
write!(s, "-{}ay", first.to_lowercase()).unwrap();
}
}
although the replace_range + write! is not very readable and not super likely to be much of a gain, so that might as well be a format!, something along the lines of:
fn to_pig_latin(s: &mut String) {
let first = s.chars().nth(0).unwrap();
if "aeiouAEIOU".contains(first) {
s.push_str("-hay")
} else {
*s = format!("{}-{}ay", &s[first.len_utf8()..], first.to_lowercase());
}
}

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 can I set a struct field value by string name?

Out of habit from interpreted programming languages, I want to rewrite many values based on their key. I assumed that I would store all the information in the struct prepared for this project. So I started iterating:
struct Container {
x: String,
y: String,
z: String
}
impl Container {
// (...)
fn load_data(&self, data: &HashMap<String, String>) {
let valid_keys = vec_of_strings![ // It's simple vector with Strings
"x", "y", "z"
] ;
for key_name in &valid_keys {
if data.contains_key(key_name) {
self[key_name] = Some(data.get(key_name);
// It's invalid of course but
// I do not know how to write it correctly.
// For example, in PHP I would write it like this:
// $this[$key_name] = $data[$key_name];
}
}
}
// (...)
}
Maybe macros? I tried to use them. key_name is always interpreted as it is, I cannot get value of key_name instead.
How can I do this without repeating the code for each value?
With macros, I always advocate starting from the direct code, then seeing what duplication there is. In this case, we'd start with
fn load_data(&mut self, data: &HashMap<String, String>) {
if let Some(v) = data.get("x") {
self.x = v.clone();
}
if let Some(v) = data.get("y") {
self.y = v.clone();
}
if let Some(v) = data.get("z") {
self.z = v.clone();
}
}
Note the number of differences:
The struct must take &mut self.
It's inefficient to check if a value is there and then get it separately.
We need to clone the value because we only only have a reference.
We cannot store an Option in a String.
Once you have your code working, you can see how to abstract things. Always start by trying to use "lighter" abstractions (functions, traits, etc.). Only after exhausting that, I'd start bringing in macros. Let's start by using stringify
if let Some(v) = data.get(stringify!(x)) {
self.x = v.clone();
}
Then you can extract out a macro:
macro_rules! thing {
($this: ident, $data: ident, $($name: ident),+) => {
$(
if let Some(v) = $data.get(stringify!($name)) {
$this.$name = v.clone();
}
)+
};
}
impl Container {
fn load_data(&mut self, data: &HashMap<String, String>) {
thing!(self, data, x, y, z);
}
}
fn main() {
let mut c = Container::default();
let d: HashMap<_, _> = vec![("x".into(), "alpha".into())].into_iter().collect();
c.load_data(&d);
println!("{:?}", c);
}
Full disclosure: I don't think this is a good idea.

Using the same iterator multiple times in Rust

Editor's note: This code example is from a version of Rust prior to 1.0 when many iterators implemented Copy. Updated versions of this code produce a different errors, but the answers still contain valuable information.
I'm trying to write a function to split a string into clumps of letters and numbers; for example, "test123test" would turn into [ "test", "123", "test" ]. Here's my attempt so far:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
match iter.peek() {
None => return bits,
Some(c) => if c.is_digit() {
bits.push(iter.take_while(|c| c.is_digit()).collect());
} else {
bits.push(iter.take_while(|c| !c.is_digit()).collect());
}
}
}
return bits;
}
However, this doesn't work, looping forever. It seems that it is using a clone of iter each time I call take_while, starting from the same position over and over again. I would like it to use the same iter each time, advancing the same iterator over all the each_times. Is this possible?
As you identified, each take_while call is duplicating iter, since take_while takes self and the Peekable chars iterator is Copy. (Only true before Rust 1.0 — editor)
You want to be modifying the iterator each time, that is, for take_while to be operating on an &mut to your iterator. Which is exactly what the .by_ref adaptor is for:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
match iter.peek().map(|c| *c) {
None => return bits,
Some(c) => if c.is_digit(10) {
bits.push(iter.by_ref().take_while(|c| c.is_digit(10)).collect());
} else {
bits.push(iter.by_ref().take_while(|c| !c.is_digit(10)).collect());
},
}
}
}
fn main() {
println!("{:?}", split("123abc456def"))
}
Prints
["123", "bc", "56", "ef"]
However, I imagine this is not correct.
I would actually recommend writing this as a normal for loop, using the char_indices iterator:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(input[start..i].to_string());
is_digit = this_is_digit;
start = i;
}
}
bits.push(input[start..].to_string());
bits
}
This form also allows for doing this with much fewer allocations (that is, the Strings are not required), because each returned value is just a slice into the input, and we can use lifetimes to state this:
pub fn split<'a>(input: &'a str) -> Vec<&'a str> {
let mut bits = vec![];
if input.is_empty() {
return bits;
}
let mut is_digit = input.chars().next().unwrap().is_digit(10);
let mut start = 0;
for (i, c) in input.char_indices() {
let this_is_digit = c.is_digit(10);
if is_digit != this_is_digit {
bits.push(&input[start..i]);
is_digit = this_is_digit;
start = i;
}
}
bits.push(&input[start..]);
bits
}
All that changed was the type signature, removing the Vec<String> type hint and the .to_string calls.
One could even write an iterator like this, to avoid having to allocate the Vec. Something like fn split<'a>(input: &'a str) -> Splits<'a> { /* construct a Splits */ } where Splits is a struct that implements Iterator<&'a str>.
take_while takes self by value: it consumes the iterator. Before Rust 1.0 it also was unfortunately able to be implicitly copied, leading to the surprising behaviour that you are observing.
You cannot use take_while for what you are wanting for these reasons. You will need to manually unroll your take_while invocations.
Here is one of many possible ways of dealing with this:
pub fn split(input: &str) -> Vec<String> {
let mut bits: Vec<String> = vec![];
let mut iter = input.chars().peekable();
loop {
let seeking_digits = match iter.peek() {
None => return bits,
Some(c) => c.is_digit(10),
};
if seeking_digits {
bits.push(take_while(&mut iter, |c| c.is_digit(10)));
} else {
bits.push(take_while(&mut iter, |c| !c.is_digit(10)));
}
}
}
fn take_while<I, F>(iter: &mut std::iter::Peekable<I>, predicate: F) -> String
where
I: Iterator<Item = char>,
F: Fn(&char) -> bool,
{
let mut out = String::new();
loop {
match iter.peek() {
Some(c) if predicate(c) => out.push(*c),
_ => return out,
}
let _ = iter.next();
}
}
fn main() {
println!("{:?}", split("test123test"));
}
This yields a solution with two levels of looping; another valid approach would be to model it as a state machine one level deep only. Ask if you aren’t sure what I mean and I’ll demonstrate.

Resources