17
fn get_links(link_nodes: Select) -> Option<String> {
let mut rel_permalink: Option<String> = for node in link_nodes {
link = String::from(node.value().attr("href")?);
return Some(link);
};
Some(rel_permalink)
}
This is what I'm trying to do, and I've been stuck with this code for an hour, I simply don't know how to put this function togheter.. Essentially I would like to take some link_nodes
and just return the link String, but I'm stuck in the use of Option with the ?
operator..
Pheraps trying to write it with match would clear things out(?)
Also I come from JavaScript in which expressions do not have their own scope, meaning I'm having troubles to understand how to get out a variable from a for loop, should I initialize the rel_permalink
variable as the for loop result?
This are the errors i get:
error[E0308]: mismatched types
--> src/main.rs:55:49
|
55 | let mut rel_permalink: Option<String> = for node in link_nodes {
| _________________________________________________^
56 | | link = String::from(node.value().attr("href")?);
57 | |
58 | | return Some(link);
59 | | };
| |_________^ expected `Option<String>`, found `()`
|
= note: expected enum `Option<String>`
found unit type `()`
note: the function expects a value to always be returned, but loops might run zero times
--> src/main.rs:55:49
|
55 | let mut rel_permalink: Option<String> = for node in link_nodes {
| ^^^^^^^^^^^^^^^^^^^^^^ this might have zero elements to iterate on
56 | link = String::from(node.value().attr("href")?);
| - if the loop doesn't execute, this value would never get returned
57 |
58 | return Some(link);
| ----------------- if the loop doesn't execute, this value would never get returned
= help: return a value for the case when the loop has zero elements to iterate on, or consider changing the return type to account for that possibility
@dontblink It's a little unclear what you want to do. It looks like Select implements into iterator. As far as I can parse your code you want to get the first node with a href element. In that case you should do:
@dontblink Here's a little explanation for the methods
-
into\_iter
: convertsSelect
into an iterator (a for loop does this implicitly)-
filter\_map
: takes an iterator and constructs an iterator from anFn(T) -\> Option\<S\>
where the emitted elements are the elements for which applying the function/closure isSome
-
next
: takes the next element of the iterator as anOption
.@dontblink Looks like there's some weird stuff between my instance and yours in the formatting. There shouldn't be any backslashes anywhere
@dontblink If you really want to do it in a loop, you could do something like
That said, your function name suggest you want _all_ links, so some kind of collection of links. Is this the case?
Hi! First of all thank you so much for the detailed explanation!
What I'm trying to do is scraping some content.
Yes I'm trying to return all links (maybe in a vector), I have a list of elements (
Select
, which actually isscraper::html::Select<'_, '_>
) which contain essentially html nodes selections, and I would like to grab each of them, extract the actual link value (&str
), convert it into an actualString
and push it firstly into a vector containing all the links and then in an istance of a struct which will contain several datas about the scraped page later.I was trying to use a for loop because that was the first structure that came to my mind, I'm finding it hard to wrap my head around ownership and error handling with rust, using the
if let
construct can be a good idea, and I didn't consider the use ofbreak
!I also managed to build the "match version" of what I was trying to achieve:
I didn't understand that I had to return the same type for each of the
Option
match arms, I thought enum variants could have different types, so if theSome
match arm returns()
, alsoNone
has to do the same..If I try with a simpler example I still cannot understand why I cannot do something like:
match is much more intuitive for a beginner, there's a lot of stuff which go under the hood with
?
Here's what you are trying to do, with a one liner:
edit: shorter and updated version:
The retain method is to get rid of all the nodes which don't have a href attribute and the fold method after it is to extract the href out of the nodes and push them into the vector.
It might work or not, I've written this from my memory and I can't exactly know what that Select is.
I also hope you begin reading The Book without half assing it.
You should use filter_map instead of the retain and later unwrapping and you don't need a fold to build a Vec from an iterator, you can just use collect for that at the end.
here, it definitely is shorter, I'll keep filter_map in mind, thanks:
@dontblink Yeah, I think it makes sense to use match as a beginner just to understand what is going on. In the end the
x?
just desugars tofor options an
for results, where that
.into()
converts the error type into the return type of function.@dontblink For stuff like this, I tend to prefer rusts iterator combinators, but I admit it's a bit more advanced. So in this case you'd do
@dontblink The idea of the iterator is to assemble all of your computation lazily and only perform it at the final step. In this case you don't actually perform any the actions until you call collect, which essentially creates a loop that goes through the original
Select
and performs the calculations and pushes the results to aVec
.@dontblink
If you want to return either a Boolean or a String you can introduce an enum
@dontblink e.g.
Note the logic here is nonsense
@dontblink It feels like you should never
OsResponseType::Bool(true)
so you could probably encode that in the enum just as the sameUnrecognised
Thank you so much, You are taking a lot of effort to answer my doubts and I really appreciate!
So essentially match can return different types, but of course I have to specify it in the function signature, wheter using an enum or other ways, this makes sense! This was a missing piece in my puzzle, I didn't consider the fact that the match return here was the function return type as well, and i had encoded
-> String
as return type.