error[E0308]: mismatched types - one type is more general than the other

If I have something like:

use clap::Parser;

#[derive(Clone, Debug, Parser)]
pub struct Args {
    /// DNS name for DNS-SD discovery of beacon servers
    #[clap(
        long,
        value_parser(addr::parse_dns_name),
    )]
    pub beacon_discovery_dns_name: addr::dns::Name<'static>,
}

I'd get an error such as:

error[E0308]: mismatched types
...
3    | #[derive(Clone, Debug, Parser)]
     |                        ^^^^^^ one type is more general than the other
...
     = note: expected trait `for<'r> <for<'r> fn(&'r str) -> std::result::Result<addr::dns::Name<'r>, addr::error::Error<'r>> {addr::parse_dns_name} as std::ops::FnOnce<(&'r str,)>>`
                found trait `for<'r> <for<'r> fn(&'r str) -> std::result::Result<addr::dns::Name<'r>, addr::error::Error<'r>> {addr::parse_dns_name} as std::ops::FnOnce<(&'r str,)>>`

Obviously I cannot change either clap's code or addr's code, so how can I adapt my code to have this working?

You can try this:

#[derive(Clone, Debug, Parser)]
pub struct Args {
    /// DNS name for DNS-SD discovery of beacon servers
    #[clap(long)]
    pub beacon_discovery_dns_name: String,
}

impl Args {
    fn to_dns(&self) -> addr::Result<addr::dns::Name> {
        addr::parse_dns_name(&self.beacon_discovery_dns_name)
    }
}
1 Like

Sorry for not being clear, but I'm trying to use clap's ValueParser, specifically: TypedValueParser in clap::builder - Rust

If I have to make another call myself, that defeats the purpose. (It's not sustainable for the caller to have to know all these methods they have to call in addition to just Args::parse.)

I think it wouldn't be possible, because the signature

pub fn parse_dns_name(input: &str) -> Result<'_, Name<'_>>

show that if you get addr::dns::Name<'static>, you must provide it with &'static str.
But AFAIK a cli struct like Args usually holds owned (non-reference) types like String.

You can test and get it from the error:

pub beacon_discovery_dns_name: &'static str,

error[E0277]: the trait bound `&str: FromStr` is not satisfied
  --> src/main.rs:13:9
   |
13 |     pub beacon_discovery_dns_name: &'static str,
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromStr` is not implemented for `&str`
   |
   = help: the trait `FromStr` is implemented for `std::string::String`
2 Likes

I've worked around the problem like this:

use std::{error::Error, fmt};

use clap::Parser;
use derive_more::{AsRef, Deref, From, Into};

#[derive(Clone, Debug, Parser)]
pub struct Args {
    /// DNS name for DNS-SD discovery of beacon servers
    #[clap(
        long,
        value_parser(parse_dns_name),
    )]
    pub beacon_discovery_dns_name: DnsName,
}

#[derive(Clone, Eq, PartialEq, Hash, Debug, From, Into, AsRef, Deref)]
#[into(owned, ref, ref_mut)]
pub struct DnsName(String);

#[derive(Debug)]
pub struct ParseError {
    kind: addr::error::Kind,
    msg: String,
}

impl TryFrom<&str> for DnsName {
    type Error = ParseError;

    fn try_from(input: &str) -> Result<Self, Self::Error> {
        addr::parse_dns_name(input).map_err(|e| ParseError::new(e.kind(), format!("{e}")))?;

        Ok(Self(input.to_owned()))
    }
}

impl Error for ParseError {}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.msg)
    }
}

impl ParseError {
    pub fn new(kind: addr::error::Kind, msg: String) -> Self {
        Self { kind, msg }
    }

    pub fn kind(&self) -> addr::error::Kind {
        self.kind
    }
}

fn parse_dns_name(input: &str) -> Result<DnsName, ParseError> {
    DnsName::try_from(input)
}

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.