Newtype approach in &mut ForeignType -> &mut T: ForeignTrait

Library A offers &mut AType
Library B takes &mut T where T: LibraryBTrait

  • I can't implement LibraryBTrait for AType because it's a foreign type.
  • It doesn't seem like I can't use the new type &mut NewType(AType) because I can't move out of the mutable reference & that's all Library A offers
  • I can't use the new type &mut NewType<'a>(&'a mut AType) as it imposes invariance rules I can't workaround with Library B
  • It doesn't seem like I can create a local trait which implements LibraryBTrait because: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local. only traits defined in the current crate can be implemented for a type parameter

Am I missing an option? Is there any way to use the new type approach here?

Not sure how to simulate a foreign type in a playground, sorry about that..

// Library A foreign type
struct Foo;

// trait fn from Library A I am implementing & can't modify
fn lib_a(foo: &mut Foo) {
    // I need to pass &mut Foo to library B
    lib_b(foo)
}

// Library B
trait LibraryBTrait {}
fn lib_b<T: LibraryBTrait>(foo: &mut T) {}

// Not allowed
// impl LibraryBTrait for Foo {}

// Not allowed
// trait LocalTrait {}
// impl<T> LibraryBTrait for T where T: LocalTrait {}

// Not feasible, I end up with invariance impossibilities
// I end up passing &mut NewType(&mut Foo)
// struct NewType<'a>(&'a mut Foo);

This is actually the most feasible option, but in a tricky way. It is sound but unsafe to transform a &mut Foo to a &mut NewType directly, without moving the contents. Luckily, bytemuck (and probably other libraries, but bytemuck is the one I know) provide you tools to do it without having to write any novel unsafe code. Plug this into your sample code and it will compile:

[dependencies]
bytemuck = { version = "1.19.0", features = ["derive"] }
use bytemuck::TransparentWrapper;

#[derive(TransparentWrapper)]
#[repr(transparent)]
struct NewType(Foo);

impl LibraryBTrait for NewType {}

fn lib_a(foo: &mut Foo) {
    lib_b(NewType::wrap_mut(foo))
}
4 Likes

Note to readers: This requires #[repr(transparent)] to be sound, which is correct in @kpreid’s example but not explicitly called out.

4 Likes

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.