From, Into, borrowing, moving

I have two crates; for simplicity I'll call them foreign and native. foregin has a struct called Config which contains an auth buffer (which contains authentication information). native contains an AuthInfo struct, which also contains authentication information.

The Config's auth and AuthInfo are not the same struct, but there's a function in native that should be able to use them both. So:

foreign

#[derive(Deserialize)]
pub struct Auth {
  pub name: Option<String>,
  pub pass: Option<String>
}

#[derive(Deserialize)]
pub struct Config {
  pub auth: Option<Auth>
}

native

pub struct AuthInfo {
  pub name: String,
  pub pass: String
}

// want foreign::Config here as well
pub fn authenticate(ai: &AuthInfo) -> Result<(), Error> {
   // ...
}

Unification

I want to make it possible to pass both foreign::Config and native::AuthInfo into native::authenticate(). To this end, I changed authenticate() to:

pub fn authenticate<A: Into<AuthInfo>>(ai: A) -> Result<(), Error> {
}

... and then implemented a From<foreign::Config> for AuthInfo in native. This is nice and all, but it forces the caller to move the Config or AuthInfo into authenticate().

I guess one can implement From<&foreign::Config> for AuthInfo. At this point I'm starting to think there has to be a better way to do this; or that it can't be done (I somehow got it into my head that I'll end up trying to take a reference to a temporary).

I know there are other trivial ways to solve it, like fn authenticate_from_foreign_config() or eliminating native::AuthInfo completely, but my question is specifically about whether it's possible to create a single native::authenticate() that can both borrow/move either a foreign::Config or a native::AuthInfo?

To summarize: it sounds like you want to be able to accept all possible authentication versions (and references thereof): native::AuthInfo as well as &native::AuthInfo, and foreign::Config as well as &foreign::Config.

I'd suggest considering going to an enum that can be any of the things you want. You can then implement From both native and foreign auth objects for the enum. Thus your single authorize function could just take a single argument of anything that is Into<AuthEnum>. And, as you say, you can even implement From<&foreign::Config> and From<&native::AuthInfo>, to hopefully get away from issues of lifetimes/references as soon as possible.

Really it sounds like you have an answer and you're just looking to reduce the layers you need to add to effectively allow for dynamic dispatch. But I think you're near the minimum of wasted code; each possible input needs an explicit path of coercion defined.

I'd also add that you might want to consider why you want to try making this function both consuming and non-consuming.

1 Like