Why is Rust still borrowing after a match statement? - rust

I have the following code:
use std::collections::HashMap;
type Elem = i64;
enum Op {
Num(Elem),
Add((String, String)),
}
type Statements = HashMap<String, Op>;
fn main() {
let mut statements = Statements::new();
statements.insert("lhs".to_string(), Op::Num(64));
statements.insert("op".to_string(), Op::Add(("lhs".to_string(), "rhs".to_string())));
statements.insert("rhs".to_string(), Op::Num(64));
let mut stack = Vec::new();
let mut op_name = "op";
loop {
let result: Elem;
match statements.get(op_name).unwrap() {
Op::Num(_) => {
panic!("{}: Did not expect Num", op_name);
},
Op::Add((lhs, rhs)) => {
if let Op::Num(lhs_value) = statements.get(lhs).unwrap() {
if let Op::Num(rhs_value) = statements.get(rhs).unwrap() {
result = lhs_value + rhs_value;
} else {
stack.push(op_name);
op_name = rhs;
continue;
}
} else {
stack.push(op_name);
op_name = lhs;
continue;
}
},
};
if let Some(new_op_name) = stack.pop() {
*(statements.get_mut(op_name).unwrap()) = Op::Num(result);
op_name = new_op_name;
} else {
println!("Result: {}", result);
return;
}
}
}
which gives me the following compiler error:
error[E0502]: cannot borrow `statements` as mutable because it is also borrowed as immutable
--> minimal.rs:43:15
|
22 | match statements.get(op_name).unwrap() {
| ----------------------- immutable borrow occurs here
...
43 | *(statements.get_mut(op_name).unwrap()) = Op::Num(result);
| ^^^^^^^^^^^-------^^^^^^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
I was expecting the borrowing to end with the match statement, since I do not see how I could still access the result of the borrowing after this statement. Why is the value still being borrowed after the match statement?
Is there something I could do to make my code above work?

In my opinion you'll find it difficult to work with strings and strings references. I would rather switch to some type for your HashMap keys that would be small, and copyable, like an integer number. This way you'll not be required to deal with references and mutability - you can just copy your integers with little to no cost.
I resolved your compilation errors, but the overall code becomes more convoluted. Basically now each insert into the hash map will give you a unique integer ID of the operation. You can only query operations from the hash map using this provided ID. It also requires you to push statements in your hash map in the order of appearance. You can't insert add(lhs, rhs) unless you already know IDs of both lhs and rhs
use std::collections::HashMap;
type Elem = i64;
type StatementId = usize;
enum Op {
Num(Elem),
Add((StatementId, StatementId)),
}
#[derive(Default)]
struct StatementsRecord {
record: HashMap<StatementId, (String, Op)>,
next_statement_id: StatementId,
}
impl StatementsRecord {
fn insert(&mut self, name: String, operation: Op) -> StatementId {
self.record.insert(self.next_statement_id, (name, operation));
self.next_statement_id += 1;
self.next_statement_id - 1 // return id of newly stored operation
}
fn get(&self, id: &StatementId) -> Option<&(String, Op)> {
self.record.get(id)
}
fn get_mut(&mut self, id: &StatementId) -> Option<&mut (String, Op)> {
self.record.get_mut(id)
}
fn update(&mut self, id: &StatementId, value: Op) {
self.get_mut(id).unwrap().1 = value;
}
}
fn main() {
let mut statements = StatementsRecord::default();
let lhs_id = statements.insert("lhs".to_string(), Op::Num(64));
let rhs_id = statements.insert("rhs".to_string(), Op::Num(64));
// 64+64 = 128
let mut op_id = statements.insert("op".to_string(), Op::Add((lhs_id, rhs_id)));
// (64+64) + (64+64) = 256
let mut op_id = statements.insert("op".to_string(), Op::Add((op_id, op_id)));
let mut stack = Vec::new();
loop {
let result: Elem;
match statements.get(&op_id).unwrap() {
(op_name, Op::Num(_)) => {
panic!("{}: Did not expect Num", op_name);
},
(op_name, Op::Add((lhs, rhs))) => {
if let (_, Op::Num(lhs_value)) = statements.get(lhs).unwrap() {
if let (_, Op::Num(rhs_value)) = statements.get(rhs).unwrap() {
result = lhs_value + rhs_value;
} else {
stack.push(op_id);
op_id = *rhs;
continue;
}
} else {
stack.push(op_id);
op_id = *lhs;
continue;
}
},
};
if let Some(new_op_id) = stack.pop() {
statements.update(&op_id, Op::Num(result));
op_id = new_op_id;
} else {
println!("Result: {}", result);
return;
}
}
}
Prints
Result: 256

Related

Passing query reference after calling get mut

A few months ago, I started learning Rust and Bevy. After a few projects my code started being repetitive, and too large to be concise and stay in a single function. To solve this problem I wrote a small macro to generate type aliases for queries and it's items and make passing their references easier.
macro_rules! mut_sys_alias {
(
$type_name:ident, $wq_q:tt, $wq_f:tt
) => {
paste::paste!{
type $type_name <'w, 's> = Query<'w, 's, $wq_q, $wq_f>;
type [<$type_name Item >]<'w> = QueryItem<'w, $wq_q>;
}
};
}
It is used like so:
mut_sys_alias! {
QContainables,
(
&'static mut Transform,
&'static GlobalTransform,
),
(
With<Containable>,
With<Draggable>,
Without<Contained>,
)
}
mut_sys_alias! {
QContained,
(
&'static mut Transform,
&'static GlobalTransform,
&'static mut Contained,
&'static Parent,
),
(
With<Containable>,
With<Draggable>,
)
}
mut_sys_alias! {
QContainers,
(
Entity,
&'static mut Container,
&'static GlobalTransform,
&'static Children,
),
()
}
This is where the problem occures:
pub fn us_container(
mut commands: Commands,
removed_dragging: RemovedComponents<Dragging>,
mut q_containables: QContainables,
mut q_contained: QContained,
mut q_containers: QContainers,
) {
for removed_dragging_entity in removed_dragging.iter() {
// Check if the current dagging entity was in a container previously
if let Ok(mut t_containable) = q_containables.get_mut(removed_dragging_entity) {
// NO it wasn't
// Loop trough all potential containers and see which is the most suitable
for mut t_container in q_containers.iter_mut() {
// Add to container
if util_add_to_container(
&mut commands,
removed_dragging_entity,
&mut t_containable,
&mut t_container,
&mut q_contained,
)
.is_ok()
{
return;
}
}
} else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
util_remove_from_container(
&mut commands,
removed_dragging_entity,
&mut t_contained,
&mut q_contained,
&mut q_containers,
);
// Previous code
// let (mut contained_tran, contained_gtran, contained_compnent, contained_parent) =
// t_contained;
// if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
// let (container_entity, mut container, _container_gtran, container_children) =
// t_container;
// commands
// .entity(removed_dragging_entity)
// .remove::<Contained>();
// contained_tran.translation = contained_gtran.translation();
// commands
// .entity(container_entity)
// .remove_children(&[removed_dragging_entity]);
// container.len -= 1;
// let idx = contained_compnent.idx;
// for child_entity in container_children.iter() {
// if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
// let (_contained_tran, _contained_gtran, mut contained_compnent, _) =
// t_contained;
// if contained_compnent.idx > idx as u8 {
// contained_compnent.idx -= 1;
// }
// }
// }
// }
}
}
}
fn util_remove_from_container(
commands: &mut Commands,
removed_dragging_entity: Entity,
t_contained: &mut QContainedItem,
q_contained: &mut QContained,
q_containers: &mut QContainers,
) {
let (contained_tran, contained_gtran, contained_compnent, contained_parent) = t_contained;
if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
let (container_entity, mut container, _container_gtran, container_children) = t_container;
commands
.entity(removed_dragging_entity)
.remove::<Contained>();
contained_tran.translation = contained_gtran.translation();
commands
.entity(container_entity)
.remove_children(&[removed_dragging_entity]);
container.len -= 1;
let idx = contained_compnent.idx;
for child_entity in container_children.iter() {
if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
let (_contained_tran, _contained_gtran, mut contained_compnent, _) = t_contained;
if contained_compnent.idx > idx as u8 {
contained_compnent.idx -= 1;
}
}
}
}
}
I tried to move the commented code to a separate function and needed to pass &mut t_contained and &mut q_contained but the compiler complained:
error[E0499]: cannot borrow `q_contained` as mutable more than once at a time
--> src\setup\container.rs:177:17
|
172 | } else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
| -------------------------------------------- first mutable borrow occurs here
173 | util_remove_from_container(
| -------------------------- first borrow later used by call
...
177 | &mut q_contained,
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`
I don't understand why the previous version of the code(the one without the function) worked, while the new one does not. I know that get_mut takes &mut self as an argument but still, I don't see why q_contained.get_mut(removed_dragging_entity) counts as a borrow inside the if let statement.
Can this be fixed somehow, and are there better ways to write modular code and not pass every component separately or in a tuple?

Borrow inside a loop

I'm trying to learn Rust after many years of C++. I have a situation where the compiler is complaining about a borrow, and it doesn't seem to matter whether it is mutable or immutable. I don't seem to be able to use self as a parameter inside a loop that start with: for item in self.func.drain(..).I've tried calling appropriate() as a function:
Self::appropriate(&self,&item,index)
and I have tried it as a method:
self.appropriate(&item,index)
but I get the same message in either case:
The function or method appropriate() is intended imply examine the relationship among its parameters and return a bool without modifying anything. How can I call either a function or method on self without violating borrowing rules?This program is a learning exercise from exercism.org and doesn't include a main() so it won't run but should almost compile except for the error in question. Here's the code I have:
use std::collections::HashMap;
pub type Value = i32;
pub type Result = std::result::Result<(), Error>;
pub struct Forth {
v: Vec<Value>,
f: HashMap<String,usize>,
s: Vec<Vec<String>>,
func: Vec<String>
}
#[derive(Debug, PartialEq)]
pub enum Error {
DivisionByZero,
StackUnderflow,
UnknownWord,
InvalidWord,
}
impl Forth {
pub fn new() -> Forth {
let mut temp: Vec<Vec<String>> = Vec::new();
temp.push(Vec::new());
Forth{v: Vec::<Value>::new(), f: HashMap::new(), s: temp, func: Vec::new()}
}
pub fn stack(&self) -> &[Value] {
&self.v
}
pub fn eval(&mut self, input: &str) -> Result {
self.v.clear();
self.s[0].clear();
let mut count = 0;
{
let temp: Vec<&str> = input.split(' ').collect();
let n = temp.len() as i32;
for x in 0..n as usize {
self.s[0].push(String::from(temp[x]));
}
}
let mut collecting = false;
let mut xlist: Vec<(usize,usize)> = Vec::new();
let mut sx: usize = 0;
let mut z: i32 = -1;
let mut x: usize;
let mut n: usize = self.s[0].len();
loop {
count += 1;
if count > 20 {break;}
z += 1;
x = z as usize;
if x >= n {break;}
z = x as i32;
let word = &self.s[sx][x];
if word == ";" {
if collecting {
collecting = false;
let index: usize = self.s.len();
self.s.push(Vec::<String>::new());
for item in self.func.drain(..) {
if self.s[index].len() > 0 &&
Self::appropriate(&self,&item,index)
{
let sx = *self.f.get(&self.s[index][0]).unwrap();
let n = self.s[sx].len();
for x in 1..n as usize {
let symbol = self.s[sx][x].clone();
self.s[index].push(symbol);
}
}
else {
self.s[index].push(item);
}
}
self.f.insert(self.s[index][0].clone(), index);
self.func.clear();
continue;
}
if 0 < xlist.len() {
(x, n) = xlist.pop().unwrap();
continue;
}
return Err(Error::InvalidWord);
}
if collecting {
self.func.push(String::from(word));
continue;
}
if Self::is_op(word) {
if self.v.len() < 2 {
return Err(Error::StackUnderflow);
}
let b = self.v.pop().unwrap();
let a = self.v.pop().unwrap();
let c = match word.as_str() {
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => {if b == 0 {return Err(Error::DivisionByZero);} a / b},
_ => 0
};
self.v.push(c);
continue;
}
match word.parse::<Value>() {
Ok(value) => { self.v.push(value); continue;},
_ => {}
}
if word == ":" {
collecting = true;
self.func.clear();
continue;
}
if word == "drop" {
if self.v.len() < 1 {
return Err(Error::StackUnderflow);
}
self.v.pop();
continue;
}
if word == "dup" {
if self.v.len() < 1 {
return Err(Error::StackUnderflow);
}
let temp = self.v[self.v.len() - 1];
self.v.push(temp);
continue;
}
if !self.f.contains_key(word) {
return Err(Error::UnknownWord);
}
xlist.push((sx,n));
sx = *self.f.get(word).unwrap();
n = self.s[sx].len();
z = 0;
}
Ok(())
}
fn is_op(input: &str) -> bool {
match input {"+"|"-"|"*"|"/" => true, _ => false}
}
fn appropriate(&self, item:&str, index:usize) -> bool
{
false
}
fn prev_def_is_short(&self, index: usize) -> bool {
if index >= self.s.len() {
false
}
else {
if let Some(&sx) = self.f.get(&self.func[0]) {
self.s[sx].len() == 2
}
else {
false
}
}
}
}
The error message relates to the call to appropriate(). I haven't even written the body of that function yet; I'd like to get the parameters right first. The compiler's complaint is:
As a subroutine call
error[E0502]: cannot borrow `self` as immutable because it is also borrowed as mutable
--> src/lib.rs:85:47
|
81 | for item in self.func.drain(..) {
| -------------------
| |
| mutable borrow occurs here
| mutable borrow later used here
...
85 | Self::appropriate(&self,&item,index)
| ^^^^^ immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
as a method call
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
--> src/lib.rs:85:29
|
81 | for item in self.func.drain(..) {
| -------------------
| |
| mutable borrow occurs here
| mutable borrow later used here
...
85 | self.appropriate(&item,index)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
Is there any canonical way to deal with this situation?
The problem is that self.func.drain() will consume the elements contained in self.func, thus an exclusive (&mut) access is needed on self.func for the entire for loop.
If during the iteration you need to pass a reference to self globally, then its func member is potentially accessible while the loop holds an exclusive access to it: Rust forbids that.
Since you use drain() in order to consume all the elements inside self.func, I suggest you swap this vector with an empty one just before the loop, then iterate on this other vector that is not anymore part of self.
No copy of the content of the vector is involved here; swap() only deals with pointers.
Here is an over-simplified version of your code, adapted consequently.
struct Forth {
func: Vec<String>,
}
impl Forth {
fn eval(&mut self) {
/*
for item in self.func.drain(..) {
self.appropriate(&self);
}
*/
let mut func = Vec::new();
std::mem::swap(&mut self.func, &mut func);
for item in func.drain(..) {
let b = self.appropriate();
println!("{:?} {:?}", item, b);
}
}
fn appropriate(&self) -> bool {
false
}
}
fn main() {
let mut f = Forth {
func: vec!["aaa".into(), "bbb".into()],
};
f.eval();
}

Linked list. Can't change borrowed value

I am on my way of learning Rust. In practical purpose I decided to make my own linked list collection. But I faced with some problems soon. I tried but can't find any way to fix this problem. Can I do this using this type of structure for linked list? The main problem I faced is implementation of deleting function.This function should delete element from the list with replacing address of the current node with address of the next node. But I can't change the value because it was borrowed already.
#[derive(Debug, PartialEq)]
enum AlmostList {
Cons(i32, Box<AlmostList>),
Nil,
}
The base of linked list is enumeration.
#[derive(Debug, PartialEq)]
struct List {
length: u32,
data: AlmostList,
}
But I want the instance of list knows its own length. Because of it I decided to use structure to store the length of the list in it.
use crate::AlmostList::{Cons, Nil};
//Implementetion of functions
impl List {
fn new() -> List {
List{ length: 0_u32, data: AlmostList::Nil }
}
fn append(&mut self, elem: i32) {
let mut alist = &mut self.data;
loop {
match alist {
Cons(_, ref mut ptr) => alist = ptr,
Nil => {
let node = Cons(elem, Box::new(Nil));
self.length += 1;
*alist = node;
break;
},
}
}
}
fn show(&self) {
let mut alist = &self.data;
if let Nil = alist {
panic!("List is empty");
}
loop {
match alist {
Cons(value, ptr) => {
print!("{} ", value);
alist = &*ptr;
},
Nil => break,
}
}
}
}
Everything was fine until I started to write function of deleting elements.This function should delete element from the list with replacing address of the current node with address of the next node.
fn delete(&mut self, number: i32) -> Option<i32> {
let mut alist = &mut self.data;
let result = loop {
match alist {
Cons(value, ptr) => {
if *value != number {
alist = ptr;
}
self.length -= 1;
*alist = *ptr.clone();
break Some(number);
},
Nil => break None,
}
};
result
}
}
Got this error:
error[E0506]: cannot assign to `*alist` because it is borrowed
--> main.rs:79:25
|
73 | Cons(value, ptr) => {
| --- borrow of `*alist` occurs here
...
79 | *alist = *ptr.clone();
| ^^^^^^
| |
| assignment to borrowed `*alist` occurs here
| borrow later used here
error[E0502]: cannot borrow `*ptr` as immutable because it is also borrowed as mutable
--> main.rs:79:35
|
75 | alist = ptr;
| --- mutable borrow occurs here
...
79 | *alist = *ptr.clone();
| ------ ^^^ immutable borrow occurs here
| |
| mutable borrow later used here
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0502, E0506.
For more information about an error, try `rustc --explain E0502`.
Can I solve this problem somehow?
Yes, you can solve this problem. I fixed it in that way:
pub fn delete(&mut self, number: i32) -> Option<i32> {
let mut alist = &mut self.data;
let result = loop {
match alist {
AlmostList::Cons(value, ref mut ptr) => {
if *value != number {
alist = ptr;
} else {
break Some(ptr);
}
}
AlmostList::Nil => break None,
}
};
if let Some(ptr) = result {
*ptr = Box::new(AlmostList::Nil);
Some(number)
} else {
None
}
}
At least the code above was compiled successfully. Full rust playground example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5bd28014bdec30e5c2e851827f15dad5
Additional hints:
write better namings. Examples: AlmostLeast -> ListNode (or just Node), Nil -> None, Cons -> Some. I have feeling that you have experience with Lisp in the past :)
format your code with cargo fmt and check with cargo clippy
any data structure implementations should not panic. Use Result type
Update:
Code above is wrong. I've implemented it using recursion: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ea6dbd3a627a59014df823be29e296b2

Temporary value dropped while borrowed while pushing elements into a Vec

I'm trying to solve the RPN calculator exercise at exercism but stumbled upon this temporary value dropped while borrowed error that I can't seem to work out.
Here's my code:
#[derive(Debug)]
pub enum CalculatorInput {
Add,
Subtract,
Multiply,
Divide,
Value(i32),
}
pub fn evaluate(inputs: &[CalculatorInput]) -> Option<i32> {
let mut stack = Vec::new();
for input in inputs {
match input {
CalculatorInput::Value(value) => {
stack.push(value);
},
operator => {
if stack.len() < 2 {
return None;
}
let second = stack.pop().unwrap();
let first = stack.pop().unwrap();
let result = match operator {
CalculatorInput::Add => first + second,
CalculatorInput::Subtract => first - second,
CalculatorInput::Multiply => first * second,
CalculatorInput::Divide => first / second,
CalculatorInput::Value(_) => return None,
};
stack.push(&result.clone());
}
}
}
if stack.len() != 1 {
None
} else {
Some(*stack.pop().unwrap())
}
}
And the error I get:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:32:29
|
32 | stack.push(&result.clone());
| ^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
...
36 | if stack.len() != 1 {
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
If I understand correctly, the variable result is no loger live outside of the for loop (outside of the operator match branch indeed), that's why I cloned it, but it still gives me the same error.
How can I make a copy of the result which is owned by the stack Vec (if that's what I should do)?
Just for reference, and in case anybody fins this useful, this is the final solution taking into account all the help received:
use crate::CalculatorInput::{Add,Subtract,Multiply,Divide,Value};
#[derive(Debug)]
pub enum CalculatorInput {
Add,
Subtract,
Multiply,
Divide,
Value(i32),
}
pub fn evaluate(inputs: &[CalculatorInput]) -> Option<i32> {
let mut stack: Vec<i32> = Vec::new();
for input in inputs {
match input {
Value(value) => {
stack.push(*value);
},
operator => {
if stack.len() < 2 {
return None;
}
let second: i32 = stack.pop().unwrap();
let first: i32 = stack.pop().unwrap();
let result: i32 = match operator {
Add => first + second,
Subtract => first - second,
Multiply => first * second,
Divide => first / second,
Value(_) => return None,
};
stack.push(result);
}
}
}
if stack.len() != 1 {
None
} else {
stack.pop()
}
}
No need to clone, because i32 implements the Copy trait.
The problem was that my vec was receiving an &i32 instead of i32, and thus rust infered it to be a Vec<&i32>.
The error is because Rust did not infer the type you expected.
In your code, the type of value is inferred to be &i32 because input is a reference of a element in inputs, and you push a value later, therefore the type of stack is inferred to be Vec<&i32>.
A best fix is to explicitly specify the type of stack:
let mut stack: Vec<i32> = Vec::new();
And because i32 has implemented Copy trait, you should never need to clone a i32 value, if it is a reference, just dereference it.
Fixed code:
#[derive(Debug)]
pub enum CalculatorInput {
Add,
Subtract,
Multiply,
Divide,
Value(i32),
}
pub fn evaluate(inputs: &[CalculatorInput]) -> Option<i32> {
let mut stack: Vec<i32> = Vec::new();
for input in inputs {
match input {
CalculatorInput::Value(value) => {
stack.push(*value);
}
operator => {
if stack.len() < 2 {
return None;
}
let second = stack.pop().unwrap();
let first = stack.pop().unwrap();
let result = match operator {
CalculatorInput::Add => first + second,
CalculatorInput::Subtract => first - second,
CalculatorInput::Multiply => first * second,
CalculatorInput::Divide => first / second,
CalculatorInput::Value(_) => return None,
};
stack.push(result);
}
}
}
if stack.len() != 1 {
None
} else {
Some(stack.pop().unwrap())
}
}
You have the same behavior with this simple exemple
fn main() {
let mut stack = Vec::new();
let a = String::from("test");
stack.push(&a.clone());
//-------- ^
println!("{:?}", stack);
}
and the good way is to not borrow when clone.
fn main() {
let mut stack = Vec::new();
let a = String::from("test");
stack.push(a.clone());
//-------- ^
println!("{:?}", stack);
}
The variable should be used like this stack.push(result.clone()); and change code like this
pub fn evaluate(inputs: &[CalculatorInput]) -> Option<i32> {
let mut stack: Vec<i32> = Vec::new();
//---------------- ^
for input in inputs {
match input {
CalculatorInput::Value(value) => {
stack.push(value.clone());
//----------------- ^
},
operator => {
if stack.len() < 2 {
return None;
}
let second = stack.pop().unwrap();
let first = stack.pop().unwrap();
let result = match operator {
CalculatorInput::Add => first + second,
CalculatorInput::Subtract => first - second,
CalculatorInput::Multiply => first * second,
CalculatorInput::Divide => first / second,
CalculatorInput::Value(_) => return None,
};
stack.push(result.clone());
//-^
}
}
}
if stack.len() != 1 {
None
} else {
Some(stack.pop().unwrap())
//------- ^
}
}

Rust concatenate &str and i32 into one variable

Sorry, but I have no idea why I am getting an error when I want to show Error as a result
pub fn content<'a>(names: Vec<Option<String>>) -> Result<bool, [&'a str; 3]> {
let mut count = 0;
for name in names {
count = count + 1;
if name.is_none() {
let message = format!("Error, position: {}", count);
return Err(["0", "Error", message.as_str()])
}
}
return Ok(true)
}
I'm converting my message variable String to &str and it should be fine, but I'm getting the following error:
error[E0515]: cannot return value referencing local variable `message`
--> src\main.rs:8:20
|
8 | return Err(["0", "Error", message.as_str()])
| ^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^^
| | |
| | `message` is borrowed here
| returns a value referencing data owned by the current function
It tells me that I cannot return the value of a reference but at the same time I don't know how to solve it. I would like to know with one explanation at a time, if possible.
The variable "message" is a local variable that will disappear after the function call (lifetime issue).
To make it compile, you can just return Result<bool, [String; 3]> as well and convert all your str to String.
Concatenate &str and i32
Generally, Rust basic types like i32 already implement Trait std::string::ToString and you can call to_string() on it directly. Have a look at my proposition():
pub fn content<'a>(names: Vec<Option<String>>) -> Result<bool, [String; 3]> {
let mut count = 0;
for name in names {
count = count + 1;
if name.is_none() {
let message = format!("Error, position: {}", count);
return Err(["0".to_string(), "Error".to_string(), message]);
}
}
return Ok(true);
}
fn proposition(names: Vec<Option<String>>) -> Result<bool, [String; 3]> {
let mut count = 0;
for name in names {
count = count + 1;
if name.is_none() {
let mut message = "Error, position: ".to_string();
message += &count.to_string(); // <-- I concat String and i32 here
return Err(["0".to_string(), "Error".to_string(), message]);
}
}
return Ok(true);
}
fn main() {
println!("{:?}", content(vec![Some("test".to_string()), None]));
println!("{:?}", proposition(vec![Some("test".to_string()), None]));
}
I get the same results as you expected:
Err(["0", "Error", "Error, position: 2"])
Err(["0", "Error", "Error, position: 2"])

Resources