Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request] export default trait functions in a swift_bridge module? #281

Open
jct-tympanon opened this issue Jul 15, 2024 · 2 comments

Comments

@jct-tympanon
Copy link

Hi there,

Is there a supported way to export a default trait function via swift_bridge? Like this,

struct Example;
trait ExampleTrait {
    fn example(&self) -> &'static str {
        "hello, world"
    }
}
impl ExampleTrait for Example {}

#[swift_bridge::bridge]
mod example {
    extern "Rust" {
        type Example;
        fn example(&self) -> &str;
    }
}

"ExampleTrait" is not in scope in the generated rust module (not surprising), so you get a compile error:

no method named `example` found for reference `&Example` in the current scope
items from traits can only be used if the trait is in scope

I've been looking for a way to trick swift_bridge into producing a "use" item for the trait, but so far no luck. The workarounds I've found all require adding a dummy "impl" block for the struct, which redefines default functions so the bridge can find them. That can be automated with macros.

A more general feature, which would cover the same behavior, would be something like reusable trait bridge definitions. Something like this:

#[swift_bridge::bridge]
mod example {
   extern "Rust" {
      trait ExampleTrait;
      fn example(&self) -> &str;
   }

   extern "Rust" {
      struct ExampleA;
      impl ExampleTrait for ExampleA;
   }

   extern "Rust" {
      struct ExampleB;
      impl ExampleTrait for ExampleB;
   }
}

Do you think either of these features would be a good fit for swift_bridge (default trait functions; reusable trait definitions)? Or is there a better approach?

@chinedufn
Copy link
Owner

Thanks for the detailed report.

Can you describe your real-world use case? My thoughts on the right solution will depend on that.

@jct-tympanon
Copy link
Author

in my case, I have two processes communicating over XPC; one written in Swift, one in Rust. the shared request and reply message types are in a Rust crate with a swift_bridge module. For transmission over XPC, I encode/decode them as JSON using serde_json.

There is a trait that looks like this, which all of the data structures implement:

pub trait JsonMessage: Sized + DeserializeOwned + Serialize {
    fn from_json(text: String) -> Result<Self> {
        Ok(serde_json::from_str(&text).map_err(MarshallingError::DeserializationError)?)
    }

    fn to_json(&self) -> Result<String> {
        Ok(serde_json::to_string(self).map_err(MarshallingError::SerializationError)?)
    }
}

I might have asked about serde<->Codable instead. But approaches for that seemed less obvious, and less generally useful, than trait support.

I know I could define a "MessageMarshaller" concrete type for these functions, instead of trait functions. But default trait functions are pretty common, as are blanket implementations. It would be nice if we could include such functions in the FFI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants