Why does a truncated string Rust print as an empty pair of parenthesis? - rust

I have
use std::io;
fn main() {
println!("CHAR COUNT");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect(
"Failed to read line",
);
let string_length = guess.len() - 2;
let correct_string_length = guess.truncate(string_length);
println!("Your text: {}", guess);
println!("Your texts wrong length is: {}", string_length);
println!("Your texts correct length: {}", correct_string_length);
}
The last line gives me
error[E0277]: the trait bound `(): std::fmt::Display` is not satisfied
--> src/main.rs:15:47
|
15 | println!("Your texts correct length: {}", correct_string_length);
| ^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter; try using `:?` instead if you are using a format string
|
= help: the trait `std::fmt::Display` is not implemented for `()`
= note: required by `std::fmt::Display::fmt`
What am I doing wrong? If I use {:?} then I get () instead of a formatted string.

When in doubt, go to the docs - here's the function signature of String::truncate:
fn truncate(&mut self, new_len: usize)
Note two things:
It takes self as &mut.
It has no return value.
From that, the problem becomes pretty clear - truncate does not return a new truncated string, it truncates the existing string in place.
This might seem a little unintuitive at first, but Rust APIs tend not to allocate new objects in memory unless you specifically ask them to - if you're never going to use guess again, then it'd be ineffecient to create a whole new String. If you wanted to make a truncated copy, then you'd need to be explicit:
let truncated = guess.clone();
truncated.truncate(string_length);
Or if you just wanted to reference part of the existing string, you could do what Ryan's answer suggests.

Just to compliment the other answers here..
Attempting to truncate a string in Rust that is not on a character boundary will cause a runtime panic.
So while this works now:
let correct_string_length = &guess[..string_length];
If you're trying to truncate a string with wider characters, your code will panic at runtime. This is especially true if you're truncating user input.. who knows what it could be. For example:
fn main() {
let s = "Hello, 世界";
println!("{}", &s[..8]); // <--- panic
}
You can use the str::is_char_boundary(usize) method to make sure you're not about to break up a wide character accidentally:
fn print_safely(s: &str, mut idx: usize) {
loop {
if s.is_char_boundary(idx) || idx >= s.len() - 1 {
break;
}
idx += 1;
}
println!("{}", &s[..idx]);
}
User input could be anything so this is just something to consider.
Playground link: http://play.integer32.com/?gist=632ff6c81c56f9ba52e0837ff25939bc&version=stable

truncate operates in place, which is why it returns (). Looks like you’re just looking for a regular non-mutating substring:
let correct_string_length = &guess[..string_length];

Related

Creating struct with values from function parameter Vec<String>and returning Vec<struct> to caller

The purpose of my program is to read questions/answers from a file (line by line), and create several structs from it, put into a Vec for further processing.
I have a rather long piece of code, which I tried to separate into several functions (full version on Playground; hopefully is valid link).
I suppose I'm not understanding a lot about borrowing, lifetimes and other things. Apart from that, the given examples from all around I've seen, I'm not able to adapt to my given problems.
Tryigin to remodel my struct fields from &str to String didn't change anything. As it was with creating Vec<Question> within get_question_list.
Function of concern is as follows:
fn get_question_list<'a>(mut questions: Vec<Question<'a>>, lines: Vec<String>) -> Vec<Question<'a>> {
let count = lines.len();
for i in (0..count).step_by(2) {
let q: &str = lines.get(i).unwrap();
let a: &str = lines.get(i + 1).unwrap();
questions.push(Question::new(q, a));
}
questions
}
This code fails with the compiler as following (excerpt):
error[E0597]: `lines` does not live long enough
--> src/main.rs:126:23
|
119 | fn get_question_list<'a>(mut questions: Vec<Question<'a>>, lines: Vec<String>) -> Vec<Question<'a>> {
| -- lifetime `'a` defined here
...
126 | let a: &str = lines.get(i + 1).unwrap();
| ^^^^^ borrowed value does not live long enough
127 |
128 | questions.push(Question::new(q, a));
| ----------------------------------- argument requires that `lines` is borrowed for `'a`
...
163 | }
| - `lines` dropped here while still borrowed
Call to get_question_list is around:
let lines: Vec<String> = content.split("\n").map(|s| s.to_string()).collect();
let counter = lines.len();
if counter % 2 != 0 {
return Err("Found lines in quiz file are not even (one question or answer is missing.).");
}
questions = get_question_list(questions, lines);
Ok(questions)
The issue is that your Questions are supposed to borrow something (hence the lifetime annotation), but lines gets moved into the function, so when you create a new question from a line, it's borrowing function-local data, which is going to be destroyed at the end of the function. As a consequence, the questions you're creating can't escape the function creating them.
Now what you could do is not move the lines into the function: lines: &[String] would have the lines be owned by the caller, which would "fix" get_question_list.
However the exact same problem exists in read_questions_from_file, and there it can not be resolved: the lines are read from a file, and thus are necessarily local to the function (unless you move the lines-reading to main and read_questions_from_file only borrows them as well).
Therefore the simplest proper fix is to change Question to own its data:
struct Question {
question: String,
answer: String
}
This way the question itself keeps its data alive, and the issue goes away.
We can improve things further though, I think:
First, we can strip out the entire mess around newlines by using String::lines, it will handle cross-platform linebreaks, and will strip them.
It also seems rather odd that get_question_list takes a vector by value only to append to it and immediately return it. A more intuitive interface would be to either:
take the "output vector" by &mut so the caller can pre-size or reuse it across multiple loads, which doesn't really seem useful in this case
or create the output vector internally, which seems like the most sensible case here
Here is what I would consider a more pleasing version: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c0d440d67654b92c75d136eba2bba0c1
fn read_questions_from_file(filename: &str) -> Result<Vec<Question>, Box<dyn Error>> {
let file_content = read_file(filename)?;
let lines: Vec<_> = file_content.lines().collect();
if lines.len() % 2 != 0 {
return Err(Box::new(OddLines));
}
let mut questions = Vec::with_capacity(lines.len() / 2);
for chunk in lines.chunks(2) {
if let [q, a] = chunk {
questions.push(Question::new(q.to_string(), a.to_string()))
} else {
unreachable!("Odd lines should already have been checked");
}
}
Ok(questions)
}
Note that I inlined / removed get_question_list as I don't think it pulls its weight at this point, and it's both trivial and very specific.
Here is a variant which works similarly but with different tradeoffs: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3b8f95aef5bcae904545617749086dbc
fn read_questions_from_file(filename: &str) -> Result<Vec<Question>, Box<dyn Error>> {
let file_content = read_file(filename)?;
let mut lines = file_content.lines();
let mut questions = Vec::new();
while let Some(q) = lines.next() {
let a = lines.next().ok_or(OddLines)?;
questions.push(Question::new(q.to_string(), a.to_string()));
}
Ok(questions)
}
it avoids collecting the lines to a Vec, but as a result has to process the file to the end before it knows that said file is suitable, and it can't preallocate Questions.
At this point, because we do not care for lines being a Vec anymore, we could operate on a BufRead and strip out read_file as well:
fn read_questions_from_file(filename: &str) -> Result<Vec<Question>, Box<dyn Error>> {
let file_content = BufReader::new(File::open(filename)?);
let mut lines = file_content.lines();
let mut questions = Vec::new();
while let Some(q) = lines.next() {
let a = lines.next().ok_or(OddLines)?;
questions.push(Question::new(q?, a?));
}
Ok(questions)
}
The extra ? are because while str::Lines yields &str, io::Lines yields Result<String, io::Error>: IO errors are reported lazily when a read is attempted, meaning every line-read could report a failure if read_to_string would have failed.
OTOH since io::Lines returns a Result<String, ...> we can use q and a directly without needing to convert them to String.

How to generate a random String of alphanumeric chars?

The first part of the question is probably pretty common and there are enough code samples that explain how to generate a random string of alphanumerics. The piece of code I use is from here.
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
fn main() {
let rand_string: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.collect();
println!("{}", rand_string);
}
This piece of code does however not compile, (note: I'm on nightly):
error[E0277]: a value of type `String` cannot be built from an iterator over elements of type `u8`
--> src/main.rs:8:10
|
8 | .collect();
| ^^^^^^^ value of type `String` cannot be built from `std::iter::Iterator<Item=u8>`
|
= help: the trait `FromIterator<u8>` is not implemented for `String`
Ok, the elements that are generated are of type u8. So I guess this is an array or vector of u8:
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
fn main() {
let r = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.collect::<Vec<_>>();
let s = String::from_utf8_lossy(&r);
println!("{}", s);
}
And this compiles and works!
2dCsTqoNUR1f0EzRV60IiuHlaM4TfK
All good, except that I would like to ask if someone could explain what exactly happens regarding the types and how this can be optimised.
Questions
.sample_iter(&Alphanumeric) produces u8 and not chars?
How can I avoid the second variable s and directly interpret an u8 as a utf-8 character? I guess the representation in memory would not change at all?
The length of these strings should always be 30. How can I optimise the heap allocation of a Vec away? Also they could actually be char[] instead of Strings.
.sample_iter(&Alphanumeric) produces u8 and not chars?
Yes, this was changed in rand v0.8. You can see in the docs for 0.7.3:
impl Distribution<char> for Alphanumeric
But then in the docs for 0.8.0:
impl Distribution<u8> for Alphanumeric
How can I avoid the second variable s and directly interpret an u8 as a utf-8 character? I guess the representation in memory would not change at all?
There are a couple of ways to do this, the most obvious being to just cast every u8 to a char:
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(|x| x as char)
.collect();
Or, using the From<u8> instance of char:
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();
Of course here, since you know every u8 must be valid UTF-8, you can use String::from_utf8_unchecked, which is faster than from_utf8_lossy (although probably around the same speed as the as char method):
let s = unsafe {
String::from_utf8_unchecked(
thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.collect::<Vec<_>>(),
)
};
If, for some reason, the unsafe bothers you and you want to stay safe, then you can use the slower String::from_utf8 and unwrap the Result so you get a panic instead of UB (even though the code should never panic or UB):
let s = String::from_utf8(
thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.collect::<Vec<_>>(),
).unwrap();
The length of these strings should always be 30. How can I optimise the heap allocation of a Vec away? Also they could actually be char[] instead of Strings.
First of all, trust me, you don't want arrays of chars. They are not fun to work with. If you want a stack string, have a u8 array then use a function like std::str::from_utf8 or the faster std::str::from_utf8_unchecked (again only usable since you know valid utf8 will be generated.)
As to optimizing the heap allocation away, refer to this answer. Basically, it's not possible with a bit of hackiness/ugliness (such as making your own function that collects an iterator into an array of 30 elements).
Once const generics are finally stabilized, there'll be a much prettier solution.
The first example in the docs for rand::distributions::Alphanumeric shows that if you want to convert the u8s into chars you should map them using the char::from function:
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
fn main() {
let rand_string: String = thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from) // map added here
.take(30)
.collect();
println!("{}", rand_string);
}
playground

How to push a value to a Vec and append it to a String at the same time?

I want to write a program that sets the shell for the system's nslookup command line program:
fn main() {
let mut v: Vec<String> = Vec::new();
let mut newstr = String::from("nslookup");
for arg in std::env::args() {
v.push(arg);
newstr.push_str(&format!(" {}", arg));
}
println!("{:?}", v);
println!("{}", newstr);
}
error[E0382]: borrow of moved value: `arg`
--> src/main.rs:6:41
|
5 | v.push(arg);
| --- value moved here
6 | newstr.push_str(&format!(" {}", arg));
| ^^^ value borrowed here after move
|
= note: move occurs because `arg` has type `std::string::String`, which does not implement the `Copy` trait
How to correct the code without traversing env::args() again?
Reverse the order of the lines that use arg:
for arg in std::env::args() {
//newstr.push_str(&format!(" {}", arg));
write!(&mut newstr, " {}", arg);
v.push(arg);
}
Vec::push takes its argument by value, which moves ownership of arg so it can't be used anymore after v.push(arg). format! and related macros implicitly borrow their arguments, so you can use arg again after using it in one of those.
If you really needed to move the same String to two different locations, you would need to add .clone(), which copies the string. But that's not necessary in this case.
Also note that format! creates a new String, which is wasteful when all you want is to add on to the end of an existing String. If you add use std::fmt::Write; to the top of your file, you can use write! instead (as shown above), which is more concise and may be more performant.
See also
What are move semantics in Rust?
error: use of moved value - should I use "&" or "mut" or something else?
Does println! borrow or own the variable?
You can do like that:
fn main() {
let args: Vec<_> = std::env::args().collect();
let s = args.join(" ");
println!("{}", s);
}
First, you create the vector, and then you create your string.

How do I parse a vector into a function?

The idea is to send a set of characters of a vector and let the function display the current correct guesses.
Here is my main:
fn main() {
let mut guessedLetters = vec![];
displayWord(guessedLetters);
}
And here is the function:
fn displayWord(correctGuess: Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
I don't know what I'm supposed to write inside the parameters of displayWord.
There's a couple of things wrong with your code.
The first error is pretty straight forward:
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ expected char, found enum `std::option::Option`
|
= note: expected type `std::vec::Vec<char>`
found type `std::vec::Vec<std::option::Option<char>>`
The function you wrote is expecting a vector a characters ... but you're passing it a vector of Option<char>. This is happening here:
guessed_Letters.push(line.chars().nth(0));
According to the documentation, the nth method returns an Option. The quick fix here is to unwrap the Option to get the underlying value:
guessed_Letters.push(line.chars().nth(0).unwrap());
Your next error is:
error[E0382]: use of moved value: `guessed_Letters`
--> src/main.rs:38:25
|
38 | displayWord(guessed_Letters);
| ^^^^^^^^^^^^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `guessed_Letters` has type `std::vec::Vec<char>`, which does not implement the `Copy` trait
This is transferring ownership of the vector on the first iteration of the loop and the compiler is telling you that subsequent iterations would be in violation of Rust's ownership rules.
The solution here is to pass the vector by reference instead:
displayWord(&guessed_Letters);
..and your method should also accept a reference:
fn displayWord(correctGuess: &Vec<char>) {
let mut currentWord = String::new();
for x in 0..5 {
currentWord.push(correctGuess[x]);
}
println!("Current guesses: {}", currentWord);
}
This can be shortened to use a slice and still work:
fn displayWord(correctGuess: &[char]) {

What does “`str` does not have a constant size known at compile-time” mean, and what's the simplest way to fix it?

I'm trying to manipulate a string derived from a function parameter and then return the result of that manipulation:
fn main() {
let a: [u8; 3] = [0, 1, 2];
for i in a.iter() {
println!("{}", choose("abc", *i));
}
}
fn choose(s: &str, pad: u8) -> String {
let c = match pad {
0 => ["000000000000000", s].join("")[s.len()..],
1 => [s, "000000000000000"].join("")[..16],
_ => ["00", s, "0000000000000"].join("")[..16],
};
c.to_string()
}
On building, I get this error:
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
--> src\main.rs:9:9
|
9 | let c = match pad {
| ^ `str` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: all local variables must have a statically known size
What's wrong here, and what's the simplest way to fix it?
TL;DR Don't use str, use &str. The reference is important.
The issue can be simplified to this:
fn main() {
let demo = "demo"[..];
}
You are attempting to slice a &str (but the same would happen for a String, &[T], Vec<T>, etc.), but have not taken a reference to the result. This means that the type of demo would be str. To fix it, add an &:
let demo = &"demo"[..];
In your broader example, you are also running into the fact that you are creating an allocated String inside of the match statement (via join) and then attempting to return a reference to it. This is disallowed because the String will be dropped at the end of the match, invalidating any references. In another language, this could lead to memory unsafety.
One potential fix is to store the created String for the duration of the function, preventing its deallocation until after the new string is created:
fn choose(s: &str, pad: u8) -> String {
let tmp;
match pad {
0 => {
tmp = ["000000000000000", s].join("");
&tmp[s.len()..]
}
1 => {
tmp = [s, "000000000000000"].join("");
&tmp[..16]
}
_ => {
tmp = ["00", s, "0000000000000"].join("");
&tmp[..16]
}
}.to_string()
}
Editorially, there's probably more efficient ways of writing this function. The formatting machinery has options for padding strings. You might even be able to just truncate the string returned from join without creating a new one.
What it means is harder to explain succinctly. Rust has a number of types that are unsized. The most prevalent ones are str and [T]. Contrast these types to how you normally see them used: &str or &[T]. You might even see them as Box<str> or Arc<[T]>. The commonality is that they are always used behind a reference of some kind.
Because these types don't have a size, they cannot be stored in a variable on the stack — the compiler wouldn't know how much stack space to reserve for them! That's the essence of the error message.
See also:
What is the return type of the indexing operation?
Return local String as a slice (&str)
Why your first FizzBuzz implementation may not work

Resources