I am trying to convert a yaml file to xml using Rust and I am not able to figure out how to fix this error regarding the use of moved value. I think I understand why this error is coming, but haven't got a clue about what to do next.
Here's the code:
struct Element {
element_name: String,
indentation_count: i16,
}
struct Attribute<'a> {
attribute_name: &'a str,
attribute_value: &'a str,
}
fn convert_yaml_to_xml(content: String, indentation_count: i16) -> String {
let mut xml_elements: Vec<Element> = vec![];
let mut attributes: Vec<Attribute> = vec![];
xml_elements.push(Element {element_name: "xmlRoot".to_string(), indentation_count: -1});
let mut target: Vec<u8> = Vec::new();
let mut xml_data_writer = EmitterConfig::new().perform_indent(true).create_writer(&mut target);
let mut attribute_written_flag = false;
let mut xml_event;
xml_event = XmlEvent::start_element("xmlRoot");
for line in content.lines() {
let current_line = line.trim();
let caps = indentation_count_regex.captures(current_line).unwrap();
let current_indentation_count = caps.get(1).unwrap().as_str().to_string().len() as i16;
if ELEMENT_REGEX.is_match(current_line) {
loop {
let current_attribute_option = attributes.pop();
match current_attribute_option {
Some(current_attribute_option) => {
xml_event.attr(current_attribute_option.attribute_name, current_attribute_option.attribute_value)
},
None => {
break;
},
};
}
xml_data_writer.write(xml_event);
// Checking if the line is an element
let caps = ELEMENT_REGEX.captures(current_line).unwrap();
let element_name = caps.get(2);
let xml_element_struct = Element {
indentation_count: current_indentation_count,
element_name: element_name.unwrap().as_str().to_string(),
};
xml_elements.push(xml_element_struct);
xml_event = XmlEvent::start_element(element_name.unwrap().as_str());
attribute_written_flag = false;
} else if ATTR_REGEX.is_match(current_line) {
// Checking if the line is an attribute
let caps = ATTR_REGEX.captures(current_line).unwrap();
let attr_name = caps.get(2);
let attr_value = caps.get(3);
// Saving attributes to a stack
attributes.push(Attribute{ attribute_name: attr_name.unwrap().as_str(), attribute_value: attr_value.unwrap().as_str() });
// xml_event.attr(attr_name.unwrap().as_str(), attr_value.unwrap().as_str());
}/* else if NEW_ATTR_SET_REGEX.is_match(current_line) {
let caps = NEW_ATTR_SET_REGEX.captures(current_line).unwrap();
let new_attr_set_name = caps.get(2);
let new_attr_set_value = caps.get(3);
current_xml_hash.insert("name".to_string(), new_attr_set_name.unwrap().as_str().to_string());
current_xml_hash.insert("value".to_string(), new_attr_set_value.unwrap().as_str().to_string());
} */
}
if attribute_written_flag {
xml_data_writer.write(xml_event);
}
for item in xml_elements.iter() {
let event = XmlEvent::end_element();
let event_name = item.element_name.to_string();
xml_data_writer.write(event.name(event_name.as_str()));
}
println!("OUTPUT");
println!("{:?}", target);
return "".to_string();
}
And here's the error:
error[E0382]: use of moved value: `xml_event`
--> src/main.rs:77:25
|
65 | let mut xml_event;
| ------------- move occurs because `xml_event` has type `StartElementBuilder<'_>`, which does not implement the `Copy` trait
...
77 | xml_event.attr(current_attribute_option.attribute_name, current_attribute_option.attribute_value)
| ^^^^^^^^^ --------------------------------------------------------------------------------------- `xml_event` moved due to this method call, in previous iteration of loop
|
note: this function takes ownership of the receiver `self`, which moves `xml_event`
--> /Users/defiant/.cargo/registry/src/github.com-1ecc6299db9ec823/xml-rs-0.8.4/src/writer/events.rs:193:24
|
193 | pub fn attr<N>(mut self, name: N, value: &'a str) -> StartElementBuilder<'a>
| ^^^^
From XmlEvent::start_element() documentation we see that it produces a StartElementBuilder<'a>.
From StartElementBuilder<'a>::attr() documentation we see that it consumes the StartElementBuilder<'a> (the first parameter is self, not &mut self) and produces a new StartElementBuilder<'a> (which is probably similar to self but considers the expected effect of .attr()).
This approach is known as the consuming builder pattern, which is used in Rust (for example std::thread::Builder).
The typical usage of such an approach consists in chaining the function calls: something.a().b().c().d() such as something is consumed by a(), its result is consumed by b(), the same about c() and finally d() does something useful with the last result.
The alternative would be to use mutable borrows in order to modify in place something but dealing with mutable borrows is known as difficult in some situations.
In your case, you can just reassign the result of .attr() to xml_event because otherwise the .attr() function would have no effect (its result is discarded) and xml_event would become unusable because it is consumed; reassigning it makes it usable again afterwards (at least i guess, i didn't try).
Related
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?
I am having issues with the concept of lifetimes in rust. I am trying to use the crate bgpkit_parser to read in a bz2 file via url link and then create a radix trie.
One field extracted from the file is the AS Path which I have named path in my code within the build_routetable function. I am having trouble as to why rust does not like let origin = clean_path.last() which takes the last element in the vector.
fn as_parser(element: &BgpElem) -> Vec<u32> {
let x = &element.as_path.as_ref().unwrap().segments[0];
let mut as_vec = &Vec::new();
let mut as_path: Vec<u32> = Vec::new();
if let AsPathSegment::AsSequence(value) = x {
as_vec = value;
}
for i in as_vec {
as_path.push(i.asn);
}
return as_path;
}
fn prefix_parser(element: &BgpElem) -> String {
let subnet_id = element.prefix.prefix.ip().to_string().to_owned();
let prefix_id = element.prefix.prefix.prefix().to_string().to_owned();
let prefix = format!("{}/{}", subnet_id, prefix_id);//.as_str();
return prefix;
}
fn get_aspath(raw_aspath: Vec<u32>) -> Vec<u32> {
let mut as_path = Vec::new();
for i in raw_aspath {
if i < 64511 {
if as_path.contains(&i) {
continue;
}
else {
as_path.push(i);
}
}
else if 65535 < i && i < 4000000000 {
if as_path.contains(&i) {
continue;
}
else {
as_path.push(i);
}
}
}
return as_path;
}
fn build_routetable(mut trie4: Trie<String, Option<&u32>>, mut trie6: Trie<String, Option<&u32>>) {
let url: &str = "http://archive.routeviews.org/route-views.chile/\
bgpdata/2022.06/RIBS/rib.20220601.0000.bz2";
let parser = BgpkitParser::new(url).unwrap();
let mut count = 0;
for elem in parser {
if elem.elem_type == bgpkit_parser::ElemType::ANNOUNCE {
let record_timestamp = &elem.timestamp;
let record_type = "A";
let peer = &elem.peer_ip;
let prefix = prefix_parser(&elem);
let path = as_parser(&elem);
let clean_path = get_aspath(path);
// Issue is on the below line
// `clean_path` does not live long enough
// borrowed value does not live long
// enough rustc E0597
// main.rs(103, 9): `clean_path` dropped
// here while still borrowed
// main.rs(77, 91): let's call the
// lifetime of this reference `'1`
// main.rs(92, 17): argument requires
// that `clean_path` is borrowed for `'1`
let origin = clean_path.last(); //issue line
if prefix.contains(":") {
trie6.insert(prefix, origin);
}
else {
trie4.insert(prefix, origin);
}
count+=1;
if count >= 10000 {
println!("{:?} | {:?} | {:?} | {:?} | {:?}",
record_type, record_timestamp, peer, prefix, path);
count=0
}
};
}
println!("Trie4 size: {:?} prefixes", trie4.len());
println!("Trie6 size: {:?} prefixes", trie6.len());
}
Short answer: you're "inserting" a reference. But what's being referenced doesn't outlive what it's being inserted into.
Longer: The hint is your trie4 argument, the signature of which is this:
mut trie4: Trie<String, Option<&u32>>
So that lives beyond the length of the loop where things are declared. This is all in the loop:
let origin = clean_path.last(); //issue line
if prefix.contains(":") {
trie6.insert(prefix, origin);
}
While origin is a Vec<u32> and that's fine, the insert method is no doubt taking a String and either an Option<&u32> or a &u32. Obviously a key/value pair. But here's your problem: the value has to live as long as the collection, but your value is the last element contained in the Vec<u32>, which goes away! So you can't put something into it that will not live as long as the "container" object! Rust has just saved you from dangling references (just like it's supposed to).
Basically, your containers should be Trie<String, Option<u32>> without the reference, and then this'll all just work fine. Your problem is that the elements are references, and not just contained regular values, and given the size of what you're containing, it's actually smaller to contain a u32 than a reference (pointer size (though actually, it'll likely be the same either way, because alignment issues)).
Also of note: trie4 and trie6 will both be gone at the end of this function call, because they were moved into this function (not references or mutable references). I hope that's what you want.
I'm new to Rust and still reading the Rust book. Below is my program.
use clap::{App, Arg};
type GenericError = Box<dyn std::error::Error + Send + Sync + 'static>;
type GenericResult<T> = Result<T, GenericError>;
fn main() -> GenericResult<()> {
let matches = App::new("test")
.arg(Arg::new("latency")
.takes_value(true))
.get_matches();
let latency_present = matches.is_present("latency");
let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
let latency_pairs: Vec<&str> = latency.split(",").collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
checker.join().unwrap()?;
Ok(())
}
When I run it, it tells me this:
error[E0597]: `matches` does not live long enough
--> src\main.rs:14:19
|
14 | let latency = matches.value_of("latency").unwrap_or("1000:10,300:30");
| ^^^^^^^--------------------
| |
| borrowed value does not live long enough
| argument requires that `matches` is borrowed for `'static`
...
30 | }
| - `matches` dropped here while still borrowed
I don't quite understand the error messages here. But I guess it's because I use latency_pairs in the checker thread and latency_pairs could get dropped while checker is still executing. Is my understanding correct? How to fix the error? I tried for (i, latency_pair) in latency_pairs.clone().iter().enumerate() { in order to pass a cloned value for the thread, but it doesn't help.
latency_pairs holds references into latency which in turn references matches. Thus cloning latency_pairs just clones the references into latency and matches.
Your code would require that latency's type is &'static str but it's actually &'a str where 'a is bound to matches' lifetime.
You can call to_owned() on latency to get an owned value and split the string inside the closure or you can call to_owned() on each of the splits collected in latency_pairs and move that Vec<String> into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
If you need to use latency_pairs outside of the closure, you can clone it before moving it into the closure:
let latency_pairs: Vec<String> = latency.split(",").map(ToOwned::to_owned).collect();
let latency_pairs_ = latency_pairs.clone();
let checker = std::thread::spawn(move || -> GenericResult<()>{
loop {
if latency_present {
for (i, latency_pair) in latency_pairs_.iter().enumerate() {
// let latency_pair: Vec<&str> = latency_pair.split(":").collect();
// let latency = latency_pair[0].parse::<f64>().unwrap();
}
}
}
});
println!("{:?}", latency_pairs);
I am working on two singly linked lists, named longer and shorter. The length of the longer one is guaranteed to be no less than the shorter one.
I pair the lists element-wise and do something to each pair. If the longer list has more unpaired elements, process the rest of them:
struct List {
next: Option<Box<List>>,
}
fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
// Pair the elements in the two lists.
while let (Some(node1), Some(node2)) = (shorter, longer) {
// Actual work elided.
shorter = node1.next;
longer = node2.next;
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
However, the compiler complains on the second while loop that
error[E0382]: use of moved value
--> src/lib.rs:13:20
|
5 | fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
| ---------- move occurs because `longer` has type `std::option::Option<std::boxed::Box<List>>`, which does not implement the `Copy` trait
6 | // Pair the elements in the two lists.
7 | while let (Some(node1), Some(node2)) = (shorter, longer) {
| ------ value moved here
...
13 | while let Some(node) = longer {
| ^^^^ value used here after move
However, I do set a new value for shorter and longer at the end of the loop, so that I will never use a moved value of them.
How should I cater to the compiler?
I think that the problem is caused by the tuple temporary in the first loop. Creating a tuple moves its components into the new tuple, and that happens even when the subsequent pattern matching fails.
First, let me write a simpler version of your code. This compiles fine:
struct Foo(i32);
fn main() {
let mut longer = Foo(0);
while let Foo(x) = longer {
longer = Foo(x + 1);
}
println!("{:?}", longer.0);
}
But if I add a temporary to the while let then I'll trigger a compiler error similar to yours:
fn fwd<T>(t: T) -> T { t }
struct Foo(i32);
fn main() {
let mut longer = Foo(0);
while let Foo(x) = fwd(longer) {
longer = Foo(x + 1);
}
println!("{:?}", longer.0);
// Error: ^ borrow of moved value: `longer`
}
The solution is to add a local variable with the value to be destructured, instead of relying on a temporary. In your code:
struct List {
next: Option<Box<List>>
}
fn drain_lists(shorter: Option<Box<List>>,
longer: Option<Box<List>>) {
// Pair the elements in the two lists.
let mut twolists = (shorter, longer);
while let (Some(node1), Some(node2)) = twolists {
// Actual work elided.
twolists = (node1.next, node2.next);
}
// Process the rest in the longer list.
let (_, mut longer) = twolists;
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
Other than getting rid of the tuple (shown by others), you can capture a mutable reference to the nodes:
while let (&mut Some(ref mut node1), &mut Some(ref mut node2)) = (&mut shorter, &mut longer) {
shorter = node1.next.take();
longer = node2.next.take();
}
The use of take() enables this to work: shorter = node1.next would complain of moving a field out of a reference, which is not allowed (it would leave the node in an undefined state). But takeing it is ok because it leaves None in the next field.
Looks like the destructuring on line 7 moves the value even when the block afterwards is not evaluated. (Edit: as #Sven Marnach pointed out in the comments, a temporary tuple gets created here which causes the move)
I've uglyfied your code to prove that point :)
struct List {
next: Option<Box<List>>
}
fn drain_lists(mut shorter: Option<Box<List>>,
mut longer: Option<Box<List>>) {
// Pair the elements in the two lists.
match(shorter, longer) {
(Some(node1), Some(node2)) => {
shorter = node1.next;
longer = node2.next;
},
(_, _) => return // without this you get the error
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
When I added the return for the default case, the code compiled.
One solution is to avoid the tuple and consequently the move of longer into the tuple.
fn actual_work(node1: &Box<List>, node2: &Box<List>) {
// Actual work elided
}
fn drain_lists(mut shorter: Option<Box<List>>, mut longer: Option<Box<List>>) {
while let Some(node1) = shorter {
if let Some(node2) = longer.as_ref() {
actual_work(&node1, node2);
}
shorter = node1.next;
longer = longer.map_or(None, move |l| {
l.next
});
}
// Process the rest in the longer list.
while let Some(node) = longer {
// Actual work elided.
longer = node.next;
}
}
I've never understood why I have received the Rust error "cannot move out of borrowed content".
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::vec::Vec;
pub struct user_type {
pub name: String,
pub ilist: Vec<i32>,
pub user_type_list: VecDeque<Option<Rc<RefCell<user_type>>>>,
pub parent: Option<Rc<RefCell<user_type>>>,
}
impl user_type {
pub fn new(name: String) -> Self {
user_type {
name: name.clone(),
ilist: Vec::new(),
user_type_list: VecDeque::new(),
parent: Option::None,
}
}
pub fn to_string(&self) -> String {
let mut result: String = String::new();
result += "name is ";
result += &self.name;
let n = self.user_type_list.len();
for iter in &self.user_type_list {
match iter {
Some(ref x) => {
let temp = x.into_inner();
let temp2 = temp.to_string();
result += &temp2[..];
}
None => panic!("to_string"),
}
result += "\n";
}
result
}
}
The full error message is:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:34:32
|
34 | let temp = x.into_inner();
| ^ cannot move out of borrowed content
What is the origin of this kind of error?
Look carefully at this code:
for iter in &self.user_type_list {
match iter {
Some(ref x) => {
let temp = x.into_inner();
let temp2 = temp.to_string();
result += &temp2[..];
}
None => panic!("to_string"),
}
result += "\n";
}
Here, you are iterating &self.user_type_list so the type of iter is actually a reference to the contained value: &Option<Rc<RefCell<user_type>>>. That is nice, because you do not want to take ownership of the container or its values.
Then you match iter to Some(ref x). Older compiler versions would fail because you are matching a reference to a non-reference, but new compilers will do as if you are matching a Option<&T> instead of a &Option<T>, if needed. That is handy, and means that you can write just Some(x) => and x will be of type &Rc<RefCell<user_type>> instead of &&Rc<..> (not that it really matters, automatic dereferencing will make those equivalent).
Now you are calling x.into_inner() with a &Rc<RefCell<..>> and that will never work. It looks like you want to get the RefCell into temp that is not needed, Rc implements Deref so you get that for free. Instead the compiler thinks you are calling RefCell::into_inner(self) -> T, but this function consumes the self to get to the contained value. And you do not own it, you just borrowed it. That is what the error message means: you are trying to consume (move out) and object you do not own (borrowd).
What you really want is just to borrow the user_type enough to call to_string():
Some(x) => {
let temp = x.borrow().to_string();
result += &temp;
}