When I try to initialize a String with the placeholder {} using the following:
let range_from: u32 = 1;
let range_to: u32 = 101;
let insert_message = String::new("Please input Your guess in the range from {} to {}.", range_from, range_to);
println!("{}", insert_message);
// snip
println!("{}", insert_message);
It throws the following Error:
supplied 3 arguments
| |
| expected 1 argument
String::new can't do that. You can use the format! macro, like this:
let insert_message = format!("Please input Your guess in the range from {} to {}.", range_from, range_to);
Or, as of Rust 1.58, you can also do it like this:
let insert_message = format!("Please input Your guess in the range from {range_from} to {range_to}.");
See this for more.
Related
How can I return a default value from an Option<&String>?
This is my sample/minimal code:
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("").or_else(|| Some("")).unwrap(); // <== I tried lots of combinations
println!("{}", result);
}
I know I could do something like this...
let value = match result {
Some(v) => v,
None => "",
};
... but I want to know if it is possible to implement it in a one-liner with or_else or unwrap_or_else?
(It is important to make the default value lazy, so it does not get computed if it is not used)
These are some of the compiler suggestions I tried (I can put them all because SO won't allow me):
7 | let result = map.get("").or_else(|| Some("") ).unwrap();
| ^^ expected struct `String`, found `str`
.
7 | let result = map.get("").or_else(|| Some(&"".to_string()) ).unwrap();
| ^^^^^^--------------^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
.
7 | let result = map.get("").or_else(|| Some(String::new()) ).unwrap();
| ^^^^^^^^^^^^^
| |
| expected `&String`, found struct `String`
|
help: consider borrowing here: `&String::new()`
.
7 | let result = map.get("").or_else(|| Some(&String::new()) ).unwrap();
| ^^^^^^-------------^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
.
and also
6 | let result = map.get("").unwrap_or_else(|| ""); // I tried lots
| ^^ expected struct `String`, found `str`
|
= note: expected reference `&String`
found reference `&'static str`
If you really need a &String as the result, you may create a String for the default value with lifetime that's long enough.
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let default_value = "default_value".to_string();
let result = map.get("").unwrap_or(&default_value);
println!("{}", result);
}
If the default value is a compile-time fixed value, the allocation of default_value can be avoided by using &str instead.
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("")
.map(String::as_str)
.unwrap_or("default_value");
println!("{}", result);
}
How can I return a default value from an Option<&String>?
It's not trivial because as you've discovered ownership gets in the way, as you need an actual String to create an &String. The cheap and easy solution to that is to just have a static empty String really:
static DEFAULT: String = String::new();
fn main() {
let map = std::collections::HashMap::<String, String>::new();
let result = map.get("").unwrap_or(&DEFAULT); // <== I tried lots of combinations
println!("{}", result);
}
String::new is const since 1.39.0, and does not allocate, so this works fine. If you want a non-empty string as default value it's not as good a solution though.
The cleaner and more regular alternative is to "downgrade" (or upgrade, depending on the POV) the &String to an &str:
let result = map.get("").map(String::as_str).unwrap_or("");
or
let result = map.get("").map(|s| &**s).unwrap_or("");
it's really not like you're losing anything here, as &String is not much more capable than &str (it does offer a few more thing e.g. String::capacity, but for the most part it exists on genericity grounds e.g. HashMap::<K, V>::get returns an &V, so if you store a String you get an &String makes sense even though it's not always quite the thing you want most).
I want something like this in Rust but I don't understand the compile errors:
fn main() {
let apple: String = String::from("apple");
let accle: String = String::from("accle");
let apple_vec: Vec<char> = apple.chars().collect();
let accle_vec: Vec<char> = accle.chars().collect();
let counter = 0;
for j in accle_vec {
for i in apple_vec {
// if i == j{
counter++;
// }
}
}
println!("counter is {}", counter);
}
I want to compare the characters of two arrays, one by one, and count every time there is a mismatch.
There are several things going on here, so let's break this down.
First error we hit is:
error: expected expression, found `+`
--> src/main.rs:12:21
|
12 | counter++;
| ^ expected expression
error: could not compile `playground` due to previous error
This means that this is invalid syntax. That is because rust does not have ++, instead we can use counter += 1 or counter = counter + 1. I'll use the first.
Making this change, we get a few errors, but concentrating on the counter, we see:
error[E0384]: cannot assign twice to immutable variable `counter`
--> src/main.rs:12:13
|
8 | let counter = 0;
| -------
| |
| first assignment to `counter`
| help: consider making this binding mutable: `mut counter`
...
12 | counter += 1;
| ^^^^^^^^^^^^ cannot assign twice to immutable variable
Some errors have detailed explanations: E0382, E0384.
For more information about an error, try `rustc --explain E0382`.
The advice we get is sound - the counter is declared as immutable and we are trying to mutate it. We should declare it as mutable. So let mut counter = 0
Lastly, we get the following error:
error[E0382]: use of moved value: `apple_vec`
--> src/main.rs:10:18
|
5 | let apple_vec: Vec<char> = apple.chars().collect();
| --------- move occurs because `apple_vec` has type `Vec<char>`, which does not implement the `Copy` trait
...
10 | for i in apple_vec {
| ^^^^^^^^^
| |
| `apple_vec` moved due to this implicit call to `.into_iter()`, in previous iteration of loop
| help: consider borrowing to avoid moving into the for loop: `&apple_vec`
|
note: this function takes ownership of the receiver `self`, which moves `apple_vec`
This is because iterating over the inner vector in this way would drain it on the first pass and on the second pass of the outer loop inner iteration would be impossible. In order to prevent this, you can borrow the vec for the iteration instead like for i in &apple_vec
Putting this all together would yield the following code:
fn main() {
let apple: String = String::from("apple");
let accle: String = String::from("accle");
let apple_vec: Vec<char> = apple.chars().collect();
let accle_vec: Vec<char> = accle.chars().collect();
let mut counter = 0;
for j in &accle_vec {
for i in &apple_vec {
if i == j {
counter += 1;
}
}
}
println!("counter is {}", counter);
}
This is how I would write the code, in a more functional manner:
use std::collections::HashSet;
fn main() {
let apple: String = String::from("appleb");
let accle: String = String::from("acclea");
// this goes through both strings at the same time
// eg. a==a p!=c p!=c
// I think maybe this was your goal
// It'll find matches where the same character is in the same position in
// both strings
// Iterate through the characters of the first string
let count1 = apple.chars()
// zip in the char from the second string
.zip(accle.chars())
// Now you have the char from the first and the second strings
// .. and you're still iterating.
// Filter so that you only keep entries where both chars are the same
.filter(|(a, b)| a == b)
// Count the output
.count();
// This is like having nested loops, where it'll compare
// the first 'a' to every letter in the second word
// eg. 'a' == 'a', 'a' != 'c'
// Using the above values, it returns one extra because there are two
// matches for 'a' in "acclea"
// This is what your original code was doing I think
// iterate through the chars of the first string
let count2 = apple.chars()
// For every char from the first string, iterate through
// *all* the chars of the second string
.flat_map(|a| accle.chars()
// Instead of just returning the char from the second string
// Return a tuple containing the char from the first string and
// the second
.map(move |b| (a, b)))
// Only accept instances where both chars are the same
.filter(|(a, b)| a == b)
// Count them
.count();
// To just see if a char from "apple" is in "accle" (anyhere)
// I would just write
let count3 = apple.chars()
// Only accept a char from "apple" if it can also be found in
// "accle"
.filter(|a| accle.chars().any(|b| *a == b))
.count();
// If speed was important and words were long, you could get all the chars
// from "accle" and put them in a HashSet
let set: HashSet<char> = accle.chars().collect();
let count4 = apple.chars()
.filter(|a| set.contains(a))
.count();
println!("Count1 is {}", count1);
println!("Count2 is {}", count2);
println!("Count3 is {}", count3);
println!("Count4 is {}", count4);
}
Playground link
With a list input of &str, I'm trying to create a String which contains a proverb based on the inputs.
Instead of String::from I also tried .to_string() but that doesn't seem to help matters.
pub fn build_proverb(list: &[&str]) -> String {
let mut string = String::from(format!("For want of a {} the {} was lost.\n", list[0], list[1]));
if list.len() > 2 {
(3..list.len() + 1).map(|x| string = string + String::from(format!("For want of a {} the {} was lost.\n", list[x], list[x-1])));
}
string = string + &(format!("And all for the want of a {}.", list[0])).to_string();
return string.to_string();
}
The error is:
error: mismatched types expected &str, found struct 'std::string::String'.
This is on the String::from(format!("For want of a {} the {} was lost.\n", list[x], list[x-1])) line.
What's confusing to me is that I'm adding a String to a String - why is it expecting a &str?
format! already returns a String, so there's no need for String::from(format!(...)), and it's also an error because it expects a &str, not a String returned by format!.
You'll also get an error in the lambda:
string = string + String::from(format!(...))
...even if you remove String::from, because it's not possible to add two Strings like that, but it is possible to add a String and a &str, so I think you should borrow like this:
string = string + &format!(...)
The same goes for this line:
string = string + &(format!("And all for the want of a {}.", list[0])).to_string();
Moreover, your usage of map won't actually execute the lambda for each element of the range. It'll just create a Map iterator, over which you'll have to iterate with a loop to actually make it execute the lambda, so you could as well iterate over the range itself and modify your string in the loop.
I'm also not terribly sure about why you're returning string.to_string() when you could've returned string itself.
I also think you have an off-by-one error in your range, so after fixing that, I ended up with this:
fn do_it(list: Vec<&str>) -> String {
let mut string = format!("For want of a {} the {} was lost.\n", list[0], list[1]);
// BTW, you don't need this `if` statement because empty ranges, like `2..2`, are perfectly fine
if list.len() > 2 {
// These ranges do not include `list.len()`, so your program won't panic, because this index doesn't exist
for x in 2 .. list.len() {
string += &format!("For want of a {} the {} was lost.\n", list[x], list[x-1])
}
}
string + &format!("And all for the want of a {}.", list[0]) // Return the result of concatenation
}
fn main() {
let list = vec!["something", "test", "StackOverflow"];
let result = do_it(list);
println!("The result is: {}", result)
}
Output (it works, but your post doesn't say what it should output, so I can't say if it's the correct result):
The result is: For want of a something the test was lost.
For want of a StackOverflow the test was lost.
And all for the want of a something.
I want to write output of my function to a file. I expected that write_fmt is what I require:
use std::{
fs::File,
io::{BufWriter, Write},
};
fn main() {
let write_file = File::create("/tmp/output").unwrap();
let mut writer = BufWriter::new(&write_file);
// From my function
let num = 1;
let factorial = 1;
writer.write_fmt("Factorial of {} = {}", num, factorial);
}
Error
error[E0061]: this function takes 1 parameter but 3 parameters were supplied
--> src/main.rs:11:12
|
11 | writer.write_fmt("Factorial of {} = {}", num, factorial);
| ^^^^^^^^^ expected 1 parameter
This seems wrong and there isn't much available in the documentation.
The documentation indicates the issue: the write_fmt method takes one argument, of type std::fmt::Arguments, which can be constructed via the format_args! macro. It also suggests the best way to use it:
The write! macro should be favored to invoke this method instead.
One calls write! (or writeln!) just like println!, but by passing the location to write to as the first argument:
write!(&mut writer, "Factorial of {} = {}", num, factorial);
(Note that the docs have a search bar at the top of each page, so one can find documentation on, for example, macros by searching for <name>! there.)
use std::fs::File;
use std::io::Write;
fn main() {
let mut w = File::create("/tmp/test.txt").unwrap();
writeln!(&mut w, "test").unwrap();
writeln!(&mut w, "formatted {}", "arguments").unwrap();
}
I want to write output of my function to a file. I expected that write_fmt is what I require:
use std::{
fs::File,
io::{BufWriter, Write},
};
fn main() {
let write_file = File::create("/tmp/output").unwrap();
let mut writer = BufWriter::new(&write_file);
// From my function
let num = 1;
let factorial = 1;
writer.write_fmt("Factorial of {} = {}", num, factorial);
}
Error
error[E0061]: this function takes 1 parameter but 3 parameters were supplied
--> src/main.rs:11:12
|
11 | writer.write_fmt("Factorial of {} = {}", num, factorial);
| ^^^^^^^^^ expected 1 parameter
This seems wrong and there isn't much available in the documentation.
The documentation indicates the issue: the write_fmt method takes one argument, of type std::fmt::Arguments, which can be constructed via the format_args! macro. It also suggests the best way to use it:
The write! macro should be favored to invoke this method instead.
One calls write! (or writeln!) just like println!, but by passing the location to write to as the first argument:
write!(&mut writer, "Factorial of {} = {}", num, factorial);
(Note that the docs have a search bar at the top of each page, so one can find documentation on, for example, macros by searching for <name>! there.)
use std::fs::File;
use std::io::Write;
fn main() {
let mut w = File::create("/tmp/test.txt").unwrap();
writeln!(&mut w, "test").unwrap();
writeln!(&mut w, "formatted {}", "arguments").unwrap();
}