How to split line in Rust - rust

I want create a function that split and return vector.
I'm trying change line type, but its dont help.
Code:
pub fn format(&self, basic_line: String) -> Vec<&str> {
let temp_line = basic_line.trim().to_lowercase();
return temp_line.split_whitespace().collect();
}
Error Output:
error[E0515]: cannot return value referencing temporary value
--> src\module.rs:55:16
|
53 | let f_line:Vec<&str> = line.trim().to_lowercase().split_whitespace().collect();
| -------------------------- temporary value created here
54 |
55 | return f_line;
| ^^^^^^ returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`

to_uppercase/to_lowercase return an owned String, so you cannot return a Vec<&str>, it is invalidated because those &str points to the function scoped created strings, return a Vec<String> instead:
pub fn format(basic_line: &str) -> Vec<String> {
basic_line
.trim()
.split_whitespace()
.map(str::to_lowercase)
.collect()
}
Playground

Related

Returns a reference to str from !format

I´m following the rust's reference book and in the chapter 10.3. Validating References with Lifetimes I'm playing with various values and ways to wrasp the concepts rigth.
But i can't found a solution to return an &str with a diferent lifetime for the longestv2 function:
fn main() {
let result:&str;
let result2:&str;
{
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
result = longestv1(string1.as_str(), string2.as_str());
result2= longestv2(string1.as_str(), string2.as_str());
}
}
println!("The longestv1 string is {}", result);
println!("The longestv2 string is {}", result2);
}
fn longestv1<'a,'b>(x: &'a str, y: &'a str) -> &'b str {
if x.len() > y.len() {
"x"
} else {
"y"
}
}
fn longestv2<'a,'b>(x: &'a str, y: &'a str) -> &'b str {
if x.len() > y.len() {
format!("{} with {}",x, x.len()).as_str()
} else {
format!("{} with {}",y, y.len()).as_str()
}
}
It will give me this errors:
error[E0515]: cannot return reference to temporary value
--> src\main.rs:31:9
|
31 | format!("{} with {}",x, x.len()).as_str()
| --------------------------------^^^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
error[E0515]: cannot return reference to temporary value
--> src\main.rs:33:9
|
33 | format!("{} with {}",y, y.len()).as_str()
| --------------------------------^^^^^^^^^
| |
| returns a reference to data owned by the current function
| temporary value created here
I want to return an &str like the first function longestv1, I know that String works but i want to wrasp the concepts
I tried to wrap and Option and then use as_ref() and other things in the web, copy() ,clone() , into() etc... but nothing trick rust to move out the temporaly value
TL;DR: You cannot.
You cannot return a reference to a temporary. Period. You can leak it, but don't.
The compiler will free your String at the end of the function, and then you'll have a dangling reference. This is bad.
Just return String.

Why my rust function get an " returns a value referencing data owned by the current function" error?

I know I can't return a borrowed value from a function, but I don't know how to solve it. This is my function:
pub fn query(&self, sql: &str, params: &[&dyn ToSql]) -> Result<ResultSet<Row>, Error> {
let pool = self.pool.clone();
let con = pool.get().unwrap();
let rows = con.query(sql, params).unwrap();
Ok(rows)
}
this is error:
error[E0515]: cannot return value referencing local variable `con`
--> BloodTranslate\src\my_db\oracle_mod.rs:62:9
|
61 | let rows = con.query(sql,params).unwrap();
| --- `con` is borrowed here
62 | Ok(rows)
| ^^^^^^^^ returns a value referencing data owned by the current function
Thanks a lot !!!
The ResultSet has a full type ResultSet<'a, Row>, so you simply cannot return it due to bound to lifetime 'a. But you can convert it into an owned type you like, e.g. a vector:
pub fn query(&self, sql: &str, params: &[&dyn ToSql]) -> Result<Vec<Row>, Error> {
let pool = self.pool.clone();
let con = pool.get().unwrap();
let rows = con.query(sql, params).unwrap();
rows.into_iter().collect()
}
Note, that there's additional allocation involved and maybe redundant if you need only a single row for example.

nom & borrowed value does not live long enough error

Trying to work with nom and iterate over my result, but I can't figure out why this borrowed value doesn't live long enough. Still new to Rust and have been head-banging for hours. Would appreciate help!
use anyhow;
use nom::{
bytes::complete::{tag, take},
character::complete::newline,
combinator::rest,
multi::separated_list1,
sequence::separated_pair,
IResult,
};
pub fn parse(raw: String) -> anyhow::Result<()> {
let (_, lines) = parse_multiline(&raw)?;
for line in lines.iter() {
let (label, value) = line;
println!("label: {}, value: {}", label, value);
}
Ok(())
}
fn parse_multiline(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
separated_list1(
newline,
separated_pair(
take(14usize),
tag(" : "),
rest,
),
)(i)
}
And the error:
error[E0597]: `raw` does not live long enough
--> src/parser/person.rs:12:38
|
12 | let (_, lines) = parse_multiline(&raw)?;
| ----------------^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `raw` is borrowed for `'static`
...
21 | }
| - `raw` dropped here while still borrowed
In parse_multiline() you return IResult<&str, Vec<(&str, &str)>>. This means that if your parse_multiline() fails, then it returns nom::Err<nom::error::Error<&str>> which has a reference to i/&raw.
Thus in parse(), parse_multiline(raw)? propagates that error further, which would return a reference to raw, which would not live long enough.
If you want to retain fn parse(raw: String), then the you can use Result::map_err() and then nom::Err::map(), to convert the &str into an owned String on error.
// nom = "6.1"
use nom::error::Error;
pub fn parse(raw: String) -> anyhow::Result<()> {
let (_, lines) = parse_multiline(&raw)
.map_err(|err| err.map(|err| Error::new(err.input.to_string(), err.code)))?;
// ...
}

How to get rid of "cannot return value referencing temporary value" error

I'm trying to implement a method that returns a RwLockReadGuard of a struct contained into a HashMap (itself in a RwLock).
The function below:
pub fn get_pair<'a>(&self, name: &str) -> Option<TradePairHandle> {
if let Ok(ref pair) = self.pairs.read() {
if let Some(p) = pair.get(name) {
if let Ok(r) = p.read() {
Some(TradePairHandle::new(r))
} else {
None
}
} else {
None
}
} else {
None
}
}
raise the following compilation error:
error[E0515]: cannot return value referencing temporary value
--> src/lib.rs:76:21
|
73 | if let Ok(ref pair) = self.pairs.read() {
| ----------------- temporary value created here
...
76 | Some(TradePairHandle::new(r))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value
referencing data owned by the current function
How to do this the correct way?
There is the full Rust playground
Thanks to Sven Marnach, I tried a different approach with owning_ref crate. Now the method get_pair looks like this:
pub fn get_pair<'a, 'me: 'a>(
&'me self,
name: &str,
) -> RwLockReadGuardRef<'a, TradePairHashMap, Arc<RwLock<TradePair>>> {
RwLockReadGuardRef::new(self.pairs.read().unwrap()).map(|pairs| pairs.get(name).unwrap())
}
And compile without errors. Thanks again!

Iterate through a whole file one character at a time

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.

Resources