I think I could handle all of my specialisation requirements, without the need for clarification or stablisation of specialisation rules, and do some other cool things, if I had an operator like as?. The as? cast operator would return an optional reference to a trait or concrete type, mutable or immutable, for example &mut Vec<String> or &dyn SomeTrait<u32>, depending on whether it is that type.
Here's a toy example using familiar traits:
fn is_iter_empty(iter: &impl (Iterator + Clone)) {
// Note the `as?` operator
if let Some(exact_iter) = iter as? &dyn ExactSizeIterator {
return exact_iter.is_empty()
}
return iter.clone().next().is_none()
}
Generally it would return Some(T) as long as it can statically determine the value. It wouldn't be able to go from &dyn SomeTrait to &dyn OtherTrait unless SomeTrait: OtherTrait.
I assume the compiler should be able to resolve this statically, even in the case of generics, due to monomorphisation. I assume this should be able to be done with a lot of the same compiler code that already resolves types and traits.
There also shouldn't be a need to specify the trait on the interface, as it's not a strict requirement on the caller. Although perhaps something will need to be added internally to ensure trait implementations are provided as-needed.
From my layman's perspective this seems to be much more straightforward than specialisation, but is not a replacement for it (it complements it). It can cover many similar use cases for library builders with a much more straightforward fallback path, and it also allows other patterns that specialisation may not cover.
This would run into the same fundamental issue as full specialization: it would make monomorphization depend on lifetime information. Consider:
fn evil(r: &u32) {
if let Some(static_ref) = r as? &'static u32 {
println!("`r` is `'static`");
} else {
println!("`r` is not `'static`");
}
}
Any feature that lets you write a function like the one above has to be disallowed in Rust. Lifetimes can only influence whether code compiles, not how it runs.
Ah thanks for the clarification, that makes sense and might be an issue.
Although I think that's fine with my prerequisite that it fails if it tries to cast to something that's not known statically. So in your example it would always return None. I agree that this is an ergonomics issue at minimum though, perhaps the compiler could warn you that it will always fail.
It's also possible I'm not understanding some edge case.
I imagine it would get the same checks as this code:
Thanks for the suggestion. Although I also don't believe it can work with unsized types (dyn). For me casting with dyn is a primary use case, so I'd need an implementation that can support it.
I'm not aware of any way to do this without having to manually implement a trait like this. I believe though there are some crates that do trait casting, but with a lot of hacks and restrictions. I don't think they would work in my case.