castaway
castaway copied to clipboard
Support specializing on trait
Auto-deref specialization can also be used to specialize on trait implementations. I'm using this a lot in a lib (1, 2, 3) and it would be awesome to abstract it away into a crate like castaway.
Here's a small proof of concept macro link (only supports matching on type directly so far)
Just my 2c, but I don't think the macro stuff should be in the same crate as typeid-based specialization. They work very differently and thus have different areas where they are applicable.
Thanks for the suggestion! This is something I'd definitely be open to adding to Castaway as long as it can be done in an expression context. The general design for Castaway is for casting to be done in expression position and not item-level position -- there are probably other crates out there if you want to simply write multiple specialized implementations of a trait.
Just my 2c, but I don't think the macro stuff should be in the same crate as typeid-based specialization. They work very differently and thus have different areas where they are applicable.
Actually I'm not totally opposed to macro-based operations. Castaway itself already leverages autoref-based macro specialization for handling reference types as well as type ID checks, as I find a combination/mix gives the best results. But my main concern would be doing things in expression position; Castaway should not be generating traits or their implementations except as necessary for internal use.
If Castaway were to support trait-based / generic casting, I would expect it to look more like this:
fn maybe_debug<T: 'static>(value: T) -> Option<String> {
if let Ok(debuggable) = cast!(value, impl Debug) {
Some(format!("{:?}", debuggable))
} else {
None
}
}
Though I am doubtful that this would be possible; we'd have to generate some type for debuggable
to resolve to which wouldn't be solvable except for maybe a trait object (which dynamic dispatch should not be present in Castaway). I'll think a bit more on possible APIs.
Though I am doubtful that this would be possible
It isn't, autoref specialization only works when the concrete type is actually known (i.e. when the generics you'd usually use are replaced by macros). See dtolnay/case-studies.
Though I am doubtful that this would be possible; we'd have to generate some type for debuggable to resolve to which wouldn't be solvable except for maybe a trait object (which dynamic dispatch should not be present in Castaway). I'll think a bit more on possible APIs.
I think once that Tracking issue for impl Trait in const and static items and let bindings #63065 is merged in, we will be able to this trivally by simply declaring let var: impl Trait = ...
.
@NobodyXu That may require some experimentation, but my instinct tells me that impl Trait
won't help us here. The reason why is that we can't guarantee that the existential will be constrained. For example, in the failure case:
struct S;
if let Ok(display) = cast!(S, impl Display) {
Some(format!("{}", display))
} else {
None
}
Struct S
does not implement Display
. When this code compiles, what we want it to compile to is an Err
since the implementation check fails, but our existential declaration of impl Display
will not be bound to anything. This will result in a compile-time error because the existential is unconstrained. The only way to avoid this would be to not generate the existential unless we already knew that the given type implemented the trait.
@NobodyXu That may require some experimentation, but my instinct tells me that
impl Trait
won't help us here. The reason why is that we can't guarantee that the existential will be constrained. For example, in the failure case:struct S; if let Ok(display) = cast!(S, impl Display) { Some(format!("{}", display)) } else { None }
Struct
S
does not implementDisplay
. When this code compiles, what we want it to compile to is anErr
since the implementation check fails, but our existential declaration ofimpl Display
will not be bound to anything. This will result in a compile-time error because the existential is unconstrained. The only way to avoid this would be to not generate the existential unless we already knew that the given type implemented the trait.
Yes indeed, I didn't think of that.
Perhaps we can do that with match_type!
though, since we can simply ignore the unmatched branches when generating code?
match_type!
doesn't do any conditional compilation; it could be thought of it as being more similar to if cfg!() {}
as opposed to #[cfg()] {}
. All branches are present and checked by the compiler, and only during later stages of compilation does rustc identify the only possible branch and discard the other branches.
match_type!
doesn't do any conditional compilation; it could be thought of it as being more similar toif cfg!() {}
as opposed to#[cfg()] {}
. All branches are present and checked by the compiler, and only during later stages of compilation does rustc identify the only possible branch and discard the other branches.
Oops, I forgot about that.
If and only if rust support if constexpr
like C++ does, then things will be a lot easier.
I'm not sure even then would this help. The problem is that in Rust, traits are not considered types (unlike interfaces in many languages). So you can't actually "compare" a type with a trait in any meaningful sense, you can only constrain generic bounds using them. So ultimately the compiler would need to provide some sort of way of checking bounds outside of the normal means of a generic parameter, which I doubt will happen. Or provide specialization directly, at which point it is possible to do some trickery to get it to work.
I'm not sure even then would this help. The problem is that in Rust, traits are not considered types (unlike interfaces in many languages). So you can't actually "compare" a type with a trait in any meaningful sense, you can only constrain generic bounds using them. So ultimately the compiler would need to provide some sort of way of checking bounds outside of the normal means of a generic parameter, which I doubt will happen. Or provide specialization directly, at which point it is possible to do some trickery to get it to work.
Providing direct support of specialization would definitely make this possible and effectively deprecate this crate.
But looking at the PR, it is going to take a long time before it happens.
Yep, very true. And now you're up to speed as to why Castaway doesn't currently support casting to traits. 😀 Unless some other novel implementation strategy is discovered.