Open Rua opened 2 years ago
https://github.com/Lokathor/bytemuck/blob/main/derive/src/traits.rs#L93 (and the similar methods on the other derives) seems to be the offender. They're always using ::bytemuck
(with the absolute prefix) rather than bytemuck
(no prefix).
I'm not super familiar with the derive code, but given that it's deriving unsafe traits i think the extra caution in the import path is probably appropriate.
If someone can submit a PR to fix this without causing potential problems with other traits under the same name ending up derivable and leading to strange bugs I'm all for that. Otherwise I might just say that in this particular case people should just use the full import path to work around this problem. Safety beats ergonomics in this case.
This is a known hard problem for proc-macros. I've been recently re-thinking about this, and here is the least-bad solution I can think of:
Make ::bytemuck
export a hidden-ish new proc-macro derive, say ZeroableUnqualified
, which acts like Zeroable
but for not prefixing bytemuck
with the ::
disambiguator.
Then any dependency that wishes to re-export Zeroable
, such as the OP's, can then do:
pub mod bytemuck {
pub use ::bytemuck::{*, ZeroableUnqualified as Zeroable};
}
A 2nd-degree dependency can then do:
pub use ::middle_crate::bytemuck;
#[derive(…, bytemuck::Zeroable)]
struct Foo …
or
pub use ::middle_crate::bytemuck::{self, Zeroable};
#[derive(…, Zeroable)]
struct Foo …
Another approach, more classic but imho more cumbersome for the 2nd-degree dependency, would be for Zeroable
to take an optional #[zeroable(crate = …)]
parameter with which to override the currently hard-coded ::bytemuck
.
This could even palliate a bit better the unsafe
-ty @Lokathor was legitimately concerned about[^macro_safety]: it's easy to change #[zeroable(crate = …)]
to #[zeroable(unsafe { crate = … })]
, for instance.
At that point the ::middle_crate
can keep using pub use ::bytemuck;
, as usual, and the 2nd-degree dependency would have to write:
pub use ::middle_crate::bytemuck::{self, Zeroable};
#[derive(…, Zeroable)]
#[zeroable(unsafe { crate = bytemuck })]
// or
#[zeroable(unsafe { crate = ::middle_crate::bytemuck })]
struct Foo …
A variant of this approach, if ::middle_crate
is motivated enough, would be for them to re-export their own Zeroable
derive, one which would emit:
#[::middle_crate::__internals::nested_derive(::middle_crate::bytemuck::Zeroable)]
#[zeroable(unsafe { crate = ::middle_crate::bytemuck })]
#input
where nested_derive
is a helper proc-macro I'll be releasing for use cases such as this one 😅
A variant of this variant would be to use #[macro_rules_derive]
, so as to "just" write, from within ::middle_crate
a macro_rules!
definition:
#[macro_export]
macro_rules! Zeroable {( $input:item ) => (
#[$crate::__internals::nested_derive($crate::bytemuck::Zeroable)]
#[zeroable(unsafe { crate = $crate::bytemuck })]
$input
)}
so that the 2nd-degree dependency could write:
use ::middle_crate::prelude::{derive, Zeroable};
#[derive(Zeroable!)]
struct Foo …
derive
would then be a re-export of a slightly tweaked version of #[macro_rules_derive]
(which I'd also have to publish, but it would be quick)[^macro_safety]: that being said, the story of macros and unsafety is known to be quite disappointing; the current realistic status quo is that the macro callsites are expected not to go to crazy with renamings and shadowings
I re-export from one crate like this:
...and then import
Zeroable
andPod
from this re-exported location. Then I addThis triggers a compiler error:
When I add
bytemuck
as a dependency directly, and useZeroable
andPod
from there, the error goes away. It seems that the internals of the derive macro can't handle an alternative export path?