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> {
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.