Private trait in public interface: sometimes allowed and sometimes not?

Maybe the problem isn't as big as I originally thought.

In my original simplified example, a simple fix would be:

-    // But this causes warning/error E0445:
-    impl<T: Internal> Dbl<T> {
+    // Fixed:
+    impl<T: Trt> Dbl<T> {

(Playground)

Nonetheless, I feel like impl<T: PrivateTrait> … might be a helpful pattern in some cases. I would like to share some of my (yet under construction) real-world code instead of the toy example above:

#[derive(Debug)]
struct EnvBackend {
    inner: *mut lmdb::MDB_env,
    db_mutex: Mutex<()>,
}

unsafe impl Send for EnvBackend {}
unsafe impl Sync for EnvBackend {}

/// Read-only handle for accessing environment that stores key-value databases
#[derive(Clone, Debug)]
pub struct EnvRo {
    backend: Arc<EnvBackend>,
}

unsafe impl Send for EnvRo {}
unsafe impl Sync for EnvRo {}

/// Read-write handle for accessing environment that stores key-value databases
#[derive(Debug)]
pub struct EnvRw {
    backend: Arc<EnvBackend>,
}

trait EnvInternal {
    fn backend(&self) -> &Arc<EnvBackend>;
}

impl EnvInternal for EnvRo {
    fn backend(&self) -> &Arc<EnvBackend> {
        &self.backend
    }
}

impl EnvInternal for EnvRw {
    fn backend(&self) -> &Arc<EnvBackend> {
        &self.backend
    }
}

/// Read-write or read-only handle for accessing environment that stores key-value databases
pub trait Env {
    /// Get new read-only handle for environment
    fn clone_ro(&self) -> EnvRo;
    /// Get maximum size of keys and duplicate data
    fn maxkeysize(&self) -> usize;
}

impl<T: EnvInternal> Env for T {
    fn clone_ro(&self) -> EnvRo {
        EnvRo {
            backend: self.backend().clone(),
        }
    }
    fn maxkeysize(&self) -> usize {
        unsafe {
            lmdb::mdb_env_get_maxkeysize(self.backend().inner)
                .try_into()
                .unwrap_or(usize::MAX)
        }
    }
}

Here, I use EnvInternal and impl<T: EnvInteral> to avoid duplicate code. I don't want to make the backend method be part of the public trait Env, because I consider the *mut lmdb::MDB_env pointer and the Mutex an implementation detail.

P.S.: I noticed that this approach has the disadvantage that all methods of Env must then have the same implementation for EnvRo and EnvRw (and cannot differ in some cases). So maybe it's not a good idea/style anyway?

In another section of my code, I have:

impl<'a> EnvBuilder<EnvRo, &'a Path> {
    /// Open environment in read-only mode
    pub fn open(&self) -> Result<EnvRo, io::Error> {
        Ok(EnvRo {
            backend: Arc::new(self.open_backend()?),
        })
    }
}

impl<'a> EnvBuilder<EnvRw, &'a Path> {
    /// Open environment in read-write mode
    pub fn open(&self) -> Result<EnvRw, io::Error> {
        Ok(EnvRw {
            backend: Arc::new(self.open_backend()?),
        })
    }
}

I wanted to do something like impl<'a, P: PrivateTrait> EnvBuilder<P, &'a Path> here, but then ran into the problems described in my original post. However, I'm not sure it really is the best way to do it. Maybe simply having two implementations of open as above is perfectly fine.

So perhaps there is no real problem with disallowing impl<T: P> S<T> { /*…*/ } if P is private. Nonetheless, I mentioned this thread in the tracking issue earlier. Maybe it is of relevance when considering making the lint deny-by-default.