this post was submitted on 08 Nov 2024
17 points (90.5% liked)

Rust Programming

8176 readers
1 users here now

founded 5 years ago
MODERATORS
 
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
you are viewing a single comment's thread
view the rest of the comments
[–] [email protected] 0 points 2 weeks ago (1 children)

@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 to

match x {  
 Some(value) =\> value,  
 None =\> return None  
}  

for options an

match x {  
 Ok(value) =\> value,  
 Err(err) =\> return Err(err.into())  
}  

for results, where that .into() converts the error type into the return type of function.

[–] [email protected] 0 points 2 weeks ago (1 children)

@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

fn get\_links(link\_nodes: scraper::html::Select\<'\_, '\_\>) -\> Vec\<String\> {  
 link\_nodes  
 .into\_iter() // convert select to iterator  
 .filter\_map(|node| node.value().attr("href")) // only select nodes with an href attribute, and select that value  
 .map(ToString::to\_string) // map that value to a string  
 .collect()  
}

[–] [email protected] 0 points 2 weeks ago (1 children)

@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 a Vec.

[–] [email protected] 0 points 2 weeks ago* (last edited 2 weeks ago) (1 children)

@dontblink

enum OperativeSystem {  
 Linux,  
 Windows,  
 Mac,  
 Unrecognised,  
}

fn find\_os(os: OperativeSystem) -\> String {  
 match os {  
 OperativeSystem::Linux =\> "This pc uses Linux".into(),  
 OperativeSystem::Windows =\> "This pc uses Windows, unlucky you!".into(),  
 OperativeSystem::Mac =\> "This pc uses Mac, I'm really sorry!".into(),  
 \_ =\> "unrecognised".into(),  
 }  
}  

If you want to return either a Boolean or a String you can introduce an enum

[–] [email protected] 0 points 2 weeks ago (1 children)

@dontblink e.g.

enum OsResponseType {  
 Message { message: String },  
 Bool(bool),  
}

fn find\_os(os: OperativeSystem) -\> OsResponseType {  
 match os {  
 OperativeSystem::Linux =\> OsResponseType::Message {  
 message: "This pc uses Linux".into(),  
 },  
 OperativeSystem::Windows =\> OsResponseType::Bool(false),  
 OperativeSystem::Mac =\> OsResponseType::Bool(true),  
 \_ =\> OsResponseType::Bool(false),  
 }  
}  

Note the logic here is nonsense

[–] [email protected] 1 points 2 weeks ago (1 children)

@dontblink It feels like you should never OsResponseType::Bool(true) so you could probably encode that in the enum just as the same Unrecognised

enum OsResponseType {  
 Message { message: String },  
 Unrecognised  
}  
[–] [email protected] 1 points 1 week ago

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.