Hello,
I have a struct implementing an Iterator
trait. However I cannot perform a fold and a map
fn from_html_to_map(html: &str) -> HashMap<String, String> {
let iterator = TagIterator::new(html);
let mut init = Parser {
map: HashMap::new(),
character_start_position: None,
tmp_char: "",
code_start_position: None,
tmp_codes: vec![],
};
let map = iterator
.fold(&mut init, |parser, element| {
// updating parser ...
parser
})
.map;
map
}
And I am getting the following error
error[E0507]: cannot move out of a mutable reference
--> examples\html_special_chars_generator.rs:53:15
|
53 | let map = iterator
| _______________^
54 | | .fold(&mut init, |parser, element| {
55 | | match element {
56 | | Elements::Start(tag, _begin, end) => {
... |
91 | | })
92 | | .map;
| |____________^ move occurs because value has type `HashMap<String, String>`, which does not implement the `Copy` trait
|
help: consider borrowing here
|
53 | let map = &iterator
54 | .fold(&mut init, |parser, element| {
55 | match element {
56 | Elements::Start(tag, _begin, end) => {
57 | if tag.name == String::from("td") && has_class(&tag, String::from("character"))
58 | {
...
Please advise.
Thank you
kornel
October 12, 2021, 12:02am
2
Did you mean to return init.map
?
It's unclear from the example what fold()
returns, but I'm guessing it returns a temporarily borrowed reference, and .map
on that is also borrowed, rather than owned object. You can't give ownership of a borrowed reference — that's stealing!
Thank you @kornel for your reply.
I'll add more details
tag_iterator.rs
file extract :
impl Iterator for TagIterator<'_> {
type Item = Elements;
fn next(&mut self) -> Option<Self::Item> {
// ...
}
}
main example file :
struct Parser<'a> {
map: HashMap<String, String>,
character_start_position: Option<usize>,
tmp_char: &'a str,
code_start_position: Option<usize>,
tmp_codes: Vec<&'a str>,
}
fn from_html_to_map(html: &str) -> HashMap<String, String> {
let has_class = |tag: &Tag, expected_class_name: String| {
if let Some(actual_class_name) = tag.classes() {
return actual_class_name.contains(expected_class_name.as_str());
}
false
};
let iterator = TagIterator::new(html);
let mut init = Parser {
map: HashMap::new(),
character_start_position: None,
tmp_char: "",
code_start_position: None,
tmp_codes: vec![],
};
let map = iterator
.fold(&mut init, |parser, element| {
match element {
Elements::Start(tag, _begin, end) => {
if tag.name == String::from("td") && has_class(&tag, String::from("character"))
{
parser.character_start_position = Some(end);
}
if tag.name == String::from("code") {
parser.code_start_position = Some(end);
}
}
Elements::End(name, begin, _end) => {
if name == String::from("td") && parser.character_start_position.is_some() {
let character = html.get(parser.character_start_position.unwrap()..begin);
//println!("- {:?}", character);
parser.character_start_position = None;
parser.tmp_char = character.unwrap();
}
if name == String::from("code") && parser.code_start_position.is_some() {
let code_found = html.get(parser.code_start_position.unwrap()..begin);
parser.code_start_position = None;
parser.tmp_codes.push(code_found.unwrap());
}
if name == String::from("tr") {
for code in &parser.tmp_codes {
parser.map.insert(parser.tmp_char.to_string(), code.to_string());
}
parser.tmp_char = "";
parser.tmp_codes.clear();
parser.character_start_position = None;
parser.code_start_position = None;
}
}
_ => {}
}
parser
})
.map;
map
}
I think you're mis-using fold
here. You really just want access to the mutable Parser
while you iterate, you don't need to replace it each time. (Note how you're trying to replace it with itself each time.)
And you do have access to it! I suggest just rewriting the iteration to use for element in iterator
.
- let mut init = Parser {
+ let mut parser = Parser {
/* ... */
};
- let map = iterator
- .fold(&mut init, |parser, element| {
+ for element in iterator {
match element {
/* ... */
_ => {}
}
-
- parser
- })
- .map;
+ }
- map
+ parser.map
Playground sketch.
3 Likes
For completeness on how fold
would work in this context and using @quinedot 's playground as a starting point, this example compiles. (Note: I removed map
from Parser
since it now is kept outside of the parser)
playground
iterator.fold(HashMap::new(), |mut map, element| {
...
for code in &parser.tmp_codes {
map.insert(parser.tmp_char.to_string(), code.to_string());
}
...
map
})
1 Like
Oh thank you @quinedot ! I was coding something too complexe
system
Closed
January 11, 2022, 10:12pm
7
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.