I try to write a code sample since an hour, who add some spaces in a String. So the main borrow a String to a function who add spaces, and then ,I wanna get the string back in the main function.
fn main() {
...
let mut line = String::new();
line = make_spaces(5, &mut line);
}
fn make_spaces(number: u8, string: &mut String) -> &mut String {
for _ in 0..number {
string.push(' ');
}
string
}
But the borrow checker give me the following error :
error[E0308]: mismatched types
--> src/main.rs:14:12
|
14 | line = make_spaces(left_spaces, &mut line);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected struct `String`, found `&mut String`
| help: try using a conversion method: `make_spaces(left_spaces, &mut line).to_string()`
I'm new with rust, and I know I don't understand borrowing. But I searched the net, and I don't understand it any better.
As I understand, I give the line ownership to make_spaces function, and then I (try to) give the ownership of string back to the main function.
Could you tell me where I'm wrong what is the solution of this problem please ? I know it's a common issue, but I don't see anything simple about that on stackoverflow.
There are two ways to do what you're trying, but you're mixing the two:
Take a mutable parameter as a reference
fn main() {
let mut line = String::new();
make_spaces(5, &mut line);
}
fn make_spaces(number: u8, string: &mut String) {
for _ in 0..number {
string.push(' ');
}
}
In that case, the caller gives you a reference to a struct so that you can modify it in place. There is no need to return anything.
Move the parameter into the function, then return the modified value
fn main() {
let mut line = String::new();
line = make_spaces(5, line);
}
fn make_spaces(number: u8, mut string: String) -> String {
for _ in 0..number {
string.push(' ');
}
string
}
In that case, the string is moved into the make_spaces function, so you need to move it back to the caller by returning it.
You don't have to return anything: you received a reference to a struct the caller of the function already has.
Just do
fn main() {
let mut line = String::new();
make_spaces(5, &mut line);
// use line here
}
fn make_spaces(number: u8, string: &mut String) {
for _ in 0..number {
string.push(' ');
}
}
Related
I'd like to understand why test1 causes an error but test2 compiles.
It seems like rust is being clever, and realising that when the .await is called directly on the async function result it knows to keep the parameter around for execution of the future but when the async is called on a separate line it can't do this.
Would love to have a link to the relevant functionality that makes this work to learn the details.
async fn do_async_thing(s: &String) {
println!("{s}");
}
fn get_string() -> String {
"sf".to_string()
}
#[tokio::test]
async fn test1() {
let a = do_async_thing(&get_string());
a.await;
}
#[tokio::test]
async fn test2() {
do_async_thing(&get_string()).await;
}
The error
error[E0716]: temporary value dropped while borrowed
--> crates/dynamo/src/error.rs:11:29
|
11 | let a = do_async_thing(&get_string());
| ^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
12 | a.await;
| - borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
It is not directly to do with async, its because the future returned from do_async_thing holds the string reference.
You can create your own future with the same result
struct DoAsyncThingFuture<'a> {
s: &'a String
}
impl<'a> Future for DoAsyncThingFuture<'a> {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
println!("{}", self.s);
Poll::Ready(())
}
}
fn do_async_thing(s: &String) -> DoAsyncThingFuture {
DoAsyncThingFuture {
s
}
}
And even get the same result without a future
fn do_sync_thing(s: &String) -> &String {
s
}
Attempting to use the return value from either of these functions will give the same error. This happens the return value of get_string does not have an owner so it is dropped after the call to do_sync_thing witch means the return reference is dangling. So as why one works and the other does not:
let a = do_sync_thing(&get_string());
println!("{}", a);
//Same as
let _temp_value = get_string();
let a = do_async_thing(&_temp_value);
drop(_temp_value);
println!("{}", a);
vs
println!("{}", do_sync_thing(&get_string()));
//Same as
let _temp_value = get_string();
println!("{}", do_async_thing(&_temp_value));
drop(_temp_value);
I'm new to rust, and am wondering why the following code doesn't result in a:
cannot borrow val as mutable more than once at a time error. It seems like by the time I've reached the second_layer function, I should have three separate references to the same original val variable:
val_ref in the main function body
val_ref2 in the first_layer function body
val_ref3 in the second_layer function body
Any help would be appreciated!
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
second_layer(val_ref2);
}
fn second_layer(val_ref3: &mut String)
{
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
}
fn main()
{
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Thanks,
References in a function calling another function temporarily do not exist for the duration of the function call.
Identifiers do not automatically live on in functions called further down the stack. Just because one function calls another does not mean that the callee should have access to the caller's local variables. You would get a not found in this scope if you tried.
What you can (try to) do is create a closure to capture the caller's environment and then reuse one of the caller's variables. But you will find that the compiler complains if you break the borrowing rules.
fn first_layer(val_ref2: &mut String) {
// println!("{}", val); // Cannot use val from main!
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
(|val_ref3: &mut String| {
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
println!("{}", val_ref2); // This is not allowed
})(val_ref2);
}
fn main() {
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Another way to think about it is with inlining. If you inline your function second_layer into first_layer you get:
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
*val_ref2 = String::from("second_layer");
println!("{}", val_ref2);
}
This is totally fine!
So then the code with the last two lines abstracted to its own function should also be totally fine.
The compiler issues an error on the following code, and I don't know how to fix it. Note, it works for the non-closure (direct call) case, it's just when I try to capture the value s in a closure that it fails. What's the right way to fix this?
fn count_letter(data : String, c : char) -> i32 {
data.chars().filter(|x| *x == c).count() as i32
}
fn main()
{
// Pretend this has to be a String, even though in this toy example it could be a str
let s = "there once was a man from nantucket".to_string();
// Works
println!("{:?}", count_letter(s, 'n'));
// error[E0507]: cannot move out of `s`, a captured variable in an `FnMut` closure
let result : Vec<(char, i32)> = ('a'..='z').map(|x| (x, count_letter(s.clone, x))).collect();
println!("{:?}", result);
}
The error is: error[E0507]: cannot move out of s, a captured variable in an FnMut closure
I gues you want this behavior:
fn count_letter(data : &str, c : char) -> i32 {
data.chars().filter(|x| *x == c).count() as i32
}
fn main()
{
// Pretend this has to be a String, even though in this toy example it could be a str
let s = "there once was a man from nantucket".to_string();
// Works
println!("{:?}", count_letter(&s, 'n'));
// Also works
let result : Vec<(char, i32)> = ('a'..='z').map(|x| (x, count_letter(&s, x))).collect();
println!("{:?}", result);
}
I'm trying to read a file into a vector, then print out a random line from that vector.
What am I doing wrong?
I'm asking here because I know I'm making a big conceptual mistake, but I'm having trouble identifying exactly where it is.
I know the error -
error[E0308]: mismatched types
26 | processor(&lines)
| ^^^^^^ expected &str, found struct std::string::String
And I see that there's a mismatch - but I don't know how to give the right type, or refactor the code for that (very short) function.
My code is below:
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path,
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
fn processor(vectr: &Vec<&str>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
fn main() {
let lines = lines_from_file("./example.txt");
for line in lines {
println!("{:?}", line);
}
processor(&lines);
}
While you're calling the processor function you're trying to pass a Vec<String> which is what the lines_from_file returns but the processor is expecting a &Vec<&str>. You can change the processor to match that expectation:
fn processor(vectr: &Vec<String>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
The main function:
fn main() {
let lines = lines_from_file("./example.txt");
for line in &lines {. // &lines to avoid moving the variable
println!("{:?}", line);
}
processor(&lines);
}
More generally, a String is not the same as a string slice &str, therefore Vec<String> is not the same as Vec<&str>. I'd recommend checking the rust book: https://doc.rust-lang.org/nightly/book/ch04-03-slices.html?highlight=String#string-slices
I'm new to Rust and I'm struggle with the concept of lifetimes. I want to make a struct that iterates through a file a character at a time, but I'm running into issues where I need lifetimes. I've tried to add them where I thought they should be but the compiler isn't happy. Here's my code:
struct Advancer<'a> {
line_iter: Lines<BufReader<File>>,
char_iter: Chars<'a>,
current: Option<char>,
peek: Option<char>,
}
impl<'a> Advancer<'a> {
pub fn new(file: BufReader<File>) -> Result<Self, Error> {
let mut line_iter = file.lines();
if let Some(Ok(line)) = line_iter.next() {
let char_iter = line.chars();
let mut advancer = Advancer {
line_iter,
char_iter,
current: None,
peek: None,
};
// Prime the pump. Populate peek so the next call to advance returns the first char
let _ = advancer.next();
Ok(advancer)
} else {
Err(anyhow!("Failed reading an empty file."))
}
}
pub fn next(&mut self) -> Option<char> {
self.current = self.peek;
if let Some(char) = self.char_iter.next() {
self.peek = Some(char);
} else {
if let Some(Ok(line)) = self.line_iter.next() {
self.char_iter = line.chars();
self.peek = Some('\n');
} else {
self.peek = None;
}
}
self.current
}
pub fn current(&self) -> Option<char> {
self.current
}
pub fn peek(&self) -> Option<char> {
self.peek
}
}
fn main() -> Result<(), Error> {
let file = File::open("input_file.txt")?;
let file_buf = BufReader::new(file);
let mut advancer = Advancer::new(file_buf)?;
while let Some(char) = advancer.next() {
print!("{}", char);
}
Ok(())
}
And here's what the compiler is telling me:
error[E0515]: cannot return value referencing local variable `line`
--> src/main.rs:37:13
|
25 | let char_iter = line.chars();
| ---- `line` is borrowed here
...
37 | Ok(advancer)
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0597]: `line` does not live long enough
--> src/main.rs:49:34
|
21 | impl<'a> Advancer<'a> {
| -- lifetime `'a` defined here
...
49 | self.char_iter = line.chars();
| -----------------^^^^--------
| | |
| | borrowed value does not live long enough
| assignment requires that `line` is borrowed for `'a`
50 | self.peek = Some('\n');
51 | } else {
| - `line` dropped here while still borrowed
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0515, E0597.
For more information about an error, try `rustc --explain E0515`.
error: could not compile `advancer`.
Some notes:
The Chars iterator borrows from the String it was created from. So you can't drop the String while the iterator is alive. But that's what happens in your new() method, the line variable owning the String disappears while the iterator referencing it is stored in the struct.
You could also try storing the current line in the struct, then it would live long enough, but that's not an option – a struct cannot hold a reference to itself.
Can you make a char iterator on a String that doesn't store a reference into the String? Yes, probably, for instance by storing the current position in the string as an integer – it shouldn't be the index of the char, because chars can be more than one byte long, so you'd need to deal with the underlying bytes yourself (using e.g. is_char_boundary() to take the next bunch of bytes starting from your current index that form a char).
Is there an easier way? Yes, if performance is not of highest importance, one solution is to make use of Vec's IntoIterator instance (which uses unsafe magic to create an object that hands out parts of itself) :
let char_iter = file_buf.lines().flat_map(|line_res| {
let line = line_res.unwrap_or(String::new());
line.chars().collect::<Vec<_>>()
});
Note that just returning line.chars() would have the same problem as the first point.
You might think that String should have a similar IntoIterator instance, and I wouldn't disagree.