Search code examples
rustasync-awaitrust-cargorust-tokioactix-web

no implementation for `dyn std::error::Error == dyn std::error::Error`


I have an async function that returns a struct after all the web parsing is done. When running the tests I'm getting the error: no implementation for `dyn std::error::Error == dyn std::error::Error` . How can I solve this or what am I doing wrong?

#[derive(Debug, PartialEq, Eq)]
pub struct ParsingResult<'a> {
    title: &'a str,
    content: String,
}
impl<'a> ParsingResult<'a> {
    pub fn new(title: &'a str, content: String) -> Self {
        ParsingResult { title, content }
    }
}
pub async fn yahoo_template<'a>(
    link_to_the_article: &str,
) -> Result<ParsingResult<'a>, Box<dyn error::Error>> {
    let resp = reqwest::get(link_to_the_article).await?.text().await?;

    let mut final_res: Vec<String> = Vec::new();
    Document::from(resp.as_str())
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .for_each(|x| {
            if x.contains("http") || x.contains("https") {
                final_res.push(String::from(x));
            }
        });
    // println!("{:?}", final_res);
    Ok(ParsingResult::new("Title", String::from("String")))
}

#[cfg(test)]
mod templates_tests {
    use super::*;
    #[tokio::test]
    async fn make_it_work() {
        let expected = Ok(ParsingResult::new("Title", String::from("String")));
        assert_eq!(
            expected,
            yahoo_template("https://ro.wikipedia.org/wiki/Guy_de_Maupassant").await
        );
    }
}

Error output:

error[E0277]: can't compare `dyn std::error::Error` with `dyn std::error::Error`
  --> src/scraper/scraping_templates.rs:47:3
   |
47 | /         assert_eq!(
48 | |             expected,
49 | |             yahoo_template("https://ro.wikipedia.org/wiki/Guy_de_Maupassant").await
50 | |         );
   | |_________^ no implementation for `dyn std::error::Error == dyn std::error::Error`
   |
   = help: the trait `PartialEq` is not implemented for `dyn std::error::Error`
   = note: required because of the requirements on the impl of `PartialEq` for `Box<dyn std::error::Error>`
   = note: 1 redundant requirement hidden
   = note: required because of the requirements on the impl of `PartialEq` for `Result<scraping_templates::ParsingResult<'_>, Box<dyn std::error::Error>>`
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

Solution

  • This error comes from the assert_eq!() invocation in async fn make_it_work().

    This assertion attempts to compare two Result<ParsingResult<'a>, Box<dyn error::Error>> values for equality. Result only implements equality comparison when the types for the Ok and Err variants can be compared for equality with themselves. This means that ParsingResult<'a> must implement PartialEq for itself (it does due to your #[derive]) and Box<dyn error::Error> must also implement PartialEq -- but Box, like Result, only implements equality comparison when the value it contains can be compared for equality, and error::Error doesn't require PartialEq (and if it did then error::Error wouldn't be object-safe anyway, so you wouldn't be able to use dyn).

    All that to say, the Err case can't be tested for equality, and so this particular instantiation of Result can't be tested for equality either. The compiler doesn't care that you're checking an Ok value, which means that it won't ever have to compare the Err variant values, it just sees two Results being compared for equality and can't find a PartialEq implementation.

    To work around this, you can modify your assertion:

    let expected = ParsingResult::new("Title", String::from("String"));
    let actual = yahoo_template("https://ro.wikipedia.org/wiki/Guy_de_Maupassant").await;
    
    assert_eq!(actual.ok(), Some(expected));
    

    The .ok() method turns a Result<V, E> into an Option<V> by mapping Ok(v) to Some(v) and Err(_) to None. This effectively discards the error, bypassing the need for the error type to have an equality comparison implementation.


    If you want to retain the error information for display in case the test fails, consider mapping the error to a type that does have an equality operation. For example, just map the error to the stringified debug representation. This will turn the Err variant into String, which obviously does provide equality comparison.

    let expected = ParsingResult::new("Title", String::from("String"));
    let actual = yahoo_template("https://ro.wikipedia.org/wiki/Guy_de_Maupassant").await;
    
    assert_eq!(actual.map_err(|e| format!("{:?}", e)), Ok(expected));