function "optional trait bound" in rust - rust

I want to have something like optional trait bound for function. Where if T implements that type - do something.
fn test<T: Eq + ?Debug>(a:T, b:T){
if a!=b{
println!("Not equal!");
if (T impl Debug){
println!("{:?} != {:?}", a, b);
}
}
}

As #user4815162342 commented, using specialization, this is possible.
I'll provide a slightly different approach from what they specified in their comment, to keep the same if ... { ... } setup that you had in your original code.
The idea is to have a trait AsMaybeDebug with an associated type Debug, which always implements Debug and a function to go from &Self to Option<&Self::Debug>:
trait AsMaybeDebug {
type Debug: Debug;
fn as_maybe_debug(&self) -> Option<&Self::Debug>;
}
After this we make a default impl for all T, with the debug type being !, the never type, and always return None.
impl<T> AsMaybeDebug for T {
default type Debug = !;
default fn as_maybe_debug(&self) -> Option<&Self::Debug> {
None
}
}
Instead of the never type, you could choose any type that always implemented Debug but still returning None.
Afterwards we specialize for T: Debug by returning Self:
impl<T: Debug> AsMaybeDebug for T {
type Debug = Self;
fn as_maybe_debug(&self) -> Option<&Self::Debug> {
Some(self)
}
}
Finally in test we just call as_maybe_debug and check if T: Debug
fn test<T: Eq>(a: T, b: T){
if a != b {
println!("Not equal!");
if let (Some(a), Some(b)) = (a.as_maybe_debug(), b.as_maybe_debug()) {
println!("{:?} != {:?}", a, b);
}
}
}
You can check in the playground both that it works and that the assembly generated for test_non_debug doesn't have any debugging calls, only the single call to std::io::_print.
It unfortunately isn't possible to retrieve the original a or b inside the if after calling as_maybe_debug.
This is due to <Self as AsMaybeDebug>::Debug not being
convertible back to Self.
This can be fixed, but not easily as it requires updates from the standard library.
Requiring AsMaybeDebug::Debug: AsRef<Self> doesn't work for 2 reasons:
There is no impl<T> AsRef<T> for T yet, this is due to specialization still being incomplete, I assume.
There is no impl<T> AsRef<T> for ! yet. Not sure if this impl can be made even with specialization or not, but it would be required.
Also, although the specialization can be unsound, I believe that the trait and it's impls cannot be used for unsoundness, you would need a specific setup to be able to generate unsoundness from it, which this lacks.

As mentioned in the comments, you're looking for the impls crate, which does exactly what you want.
if impls!(T: Debug) {
...
}
Just for the sake of completeness, here's how you do it without an external crate dependency. I'm paraphrasing from the way the impls developer explains the trick.
Let's say we want to check whether some type implements Debug. First, let's define the "base case".
trait NotDebug {
const IMPLS: bool = false;
}
We'll also provide a blanket implementation so that all types (which don't have a better answer) have a IMPLS constant equal to false.
impl<T> NotDebug for T {}
Now, let's make a simple type with a single generic type parameter.
struct IsDebug<T>(std::marker::PhantomData<T>);
PhantomData is conceptually nonexistent and exists only to anchor the generic type T to our IsDebug. We can think of IsDebug as being effectively a singleton struct.
Now, we would like IsDebug::<T>::IMPLS to be true if (and only if) T implements Debug. Currently, IsDebug::<T>::IMPLS is always false, by a blanket implementation of NotDebug. But we can specify an inherent impl that applies conditionally.
impl<T: Debug> IsDebug<T> {
const IMPLS: bool = true;
}
Since this is an impl on IsDebug itself, not on a trait implementation, it takes precedent over the NotDebug blanket implementation. In any case where T: Debug, the inherent impl kicks in and we get true. In any other case, the inherent impl fails, so we get the fallback blanket implementation which gives false.
Try it in the Rust Playground!

Related

How to assign an impl trait in a struct?

Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).

What is a canonical approach for having lite concrete-specific behaviour in Rust? [duplicate]

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.
It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

How to offer an API that stores values of different types and can return them with the original type restored?

I want to offer a safe API like below FooManager. It should be able to store arbitrary user-defined values that implement a trait Foo. It should also be able to hand them back later - not as trait object (Box<dyn Foo>) but as the original type (Box<T> where T: Foo). At least conceptually it should be possible to offer this as a safe API, by using generic handles (Handle<T>), see below.
Additional criteria:
The solution should work in stable Rust (internal usage of unsafe blocks is perfectly okay though).
I don't want to modify the trait Foo, as e.g. suggested in How to get a reference to a concrete type from a trait object?. It should work without adding a method as_any(). Reasoning: Foo shouldn't have any knowledge about the fact that it might be stored in containers and be restored to the actual type.
trait Foo {}
struct Handle<T> {
// ...
}
struct FooManager {
// ...
}
impl FooManager {
// A real-world API would complain if the value is already stored.
pub fn keep_foo<T: Foo>(&mut self, foo: Box<T>) -> Handle<T> {
// ...
}
// In a real-world API this would return an `Option`.
pub fn return_foo<T: Foo>(&mut self, handle: Handle<T>) -> Box<T> {
// ...
}
}
I came up with this (Rust Playground) but not sure if there's a better way or if it's safe even. What do you think of that approach?

Rust - downcast dyn trait [duplicate]

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.
It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

Retrieving generic struct from trait object [duplicate]

How do I get Box<B> or &B or &Box<B> from the a variable in this code:
trait A {}
struct B;
impl A for B {}
fn main() {
let mut a: Box<dyn A> = Box::new(B);
let b = a as Box<B>;
}
This code returns an error:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn A>` as `std::boxed::Box<B>`
--> src/main.rs:8:13
|
8 | let b = a as Box<B>;
| ^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
There are two ways to do downcasting in Rust. The first is to use Any. Note that this only allows you to downcast to the exact, original concrete type. Like so:
use std::any::Any;
trait A {
fn as_any(&self) -> &dyn Any;
}
struct B;
impl A for B {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let a: Box<dyn A> = Box::new(B);
// The indirection through `as_any` is because using `downcast_ref`
// on `Box<A>` *directly* only lets us downcast back to `&A` again.
// The method ensures we get an `Any` vtable that lets us downcast
// back to the original, concrete type.
let b: &B = match a.as_any().downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
}
The other way is to implement a method for each "target" on the base trait (in this case, A), and implement the casts for each desired target type.
Wait, why do we need as_any?
Even if you add Any as a requirement for A, it's still not going to work correctly. The first problem is that the A in Box<dyn A> will also implement Any... meaning that when you call downcast_ref, you'll actually be calling it on the object type A. Any can only downcast to the type it was invoked on, which in this case is A, so you'll only be able to cast back down to &dyn A which you already had.
But there's an implementation of Any for the underlying type in there somewhere, right? Well, yes, but you can't get at it. Rust doesn't allow you to "cross cast" from &dyn A to &dyn Any.
That is what as_any is for; because it's something only implemented on our "concrete" types, the compiler doesn't get confused as to which one it's supposed to invoke. Calling it on an &dyn A causes it to dynamically dispatch to the concrete implementation (again, in this case, B::as_any), which returns an &dyn Any using the implementation of Any for B, which is what we want.
Note that you can side-step this whole problem by just not using A at all. Specifically, the following will also work:
fn main() {
let a: Box<dyn Any> = Box::new(B);
let _: &B = match a.downcast_ref::<B>() {
Some(b) => b,
None => panic!("&a isn't a B!")
};
}
However, this precludes you from having any other methods; all you can do here is downcast to a concrete type.
As a final note of potential interest, the mopa crate allows you to combine the functionality of Any with a trait of your own.
It should be clear that the cast can fail if there is another type C implementing A and you try to cast Box<C> into a Box<B>. I don't know your situation, but to me it looks a lot like you are bringing techniques from other languages, like Java, into Rust. I've never encountered this kind of Problem in Rust -- maybe your code design could be improved to avoid this kind of cast.
If you want, you can "cast" pretty much anything with mem::transmute. Sadly, we will have a problem if we just want to cast Box<A> to Box<B> or &A to &B because a pointer to a trait is a fat-pointer that actually consists of two pointers: One to the actual object, one to the vptr. If we're casting it to a struct type, we can just ignore the vptr. Please remember that this solution is highly unsafe and pretty hacky -- I wouldn't use it in "real" code.
let (b, vptr): (Box<B>, *const ()) = unsafe { std::mem::transmute(a) };
EDIT: Screw that, it's even more unsafe than I thought. If you want to do it correctly this way you'd have to use std::raw::TraitObject. This is still unstable though. I don't think that this is of any use to OP; don't use it!
There are better alternatives in this very similar question: How to match trait implementors

Resources