How can I implement a function differently depending on if a generic type implements a trait or not?

Multi tool use
How can I implement a function differently depending on if a generic type implements a trait or not?
I'd like to make the implementation of do_something
conditional based on if the generic type T
implements Debug
or not. Is there any way to do something like this?
do_something
T
Debug
struct A(i32);
#[derive(Debug)]
struct B(i32);
struct Foo<T> {
data: T,
/* more fields */
}
impl<T> Foo<T> {
fn do_something(&self) {
/* ... */
println!("Success!");
}
fn do_something(&self)
where
T: Debug,
{
/* ... */
println!("Success on {:?}", self.data);
}
}
fn main() {
let foo = Foo {
data: A(3), /* ... */
};
foo.do_something(); // should call first implementation, because A
// doesn't implement Debug
let foo = Foo {
data: B(2), /* ... */
};
foo.do_something(); // should call second implementation, because B
// does implement Debug
}
I think one way to do this is to create a trait where we have to define do_something(&Self)
, but I'm not sure. My code snippet is what I will try first.
do_something(&Self)
@Stargateur what I mean is to do A if T implements a Trait, and do B if T doesn't implement a Trait, all of this under one common name like
do_something(&Self)
– Kevin Del Castillo Ramirez
Jul 2 at 17:43
do_something(&Self)
It's not possible both must implement the trait in this context. :/ Maybe you should read about trait and how to use them.
– Stargateur
Jul 2 at 17:51
Yeah, I supposed it, but just wondering if I didn't know something ultra advanced, but it will be cool to see something like
if T impl Debug { A } else { B }
inside functions, I think it's possible because its like conditional compilation, all of this can be checked at compile time.– Kevin Del Castillo Ramirez
Jul 2 at 17:59
if T impl Debug { A } else { B }
1 Answer
1
Here is a solution based on the nightly feature specialization:
#![feature(specialization)]
use std::fmt::Debug;
struct A(i32);
#[derive(Debug)]
struct B(i32);
struct Foo<T> {
data: T,
/* more fields */
}
trait Do {
fn do_something(&self);
}
impl<T> Do for Foo<T> {
default fn do_something(&self) {
/* ... */
println!("Success!");
}
}
impl<T> Do for Foo<T>
where
T: Debug,
{
fn do_something(&self) {
/* ... */
println!("Success on {:?}", self.data);
}
}
fn main() {
let foo = Foo {
data: A(3), /* ... */
};
foo.do_something(); // should call first implementation, because A
// doesn't implement Debug
let foo = Foo {
data: B(2), /* ... */
};
foo.do_something(); // should call second implementation, because B
// does implement Debug
}
The first step is to create a trait which defines do_something(&self)
. Now, we define two impl
s of this trait for Foo<T>
: a generic "parent" impl
which is implemented for all T
and a specialized "child" impl
which is only implemented for the subset where T
implements Debug
. The child impl
may specialize items from the parent impl
. These items we want to specialize need to be marked with the default
keyword in the parent impl
. In your example, we want to specialize do_something
.
do_something(&self)
impl
Foo<T>
impl
T
impl
T
Debug
impl
impl
default
impl
do_something
I had something like this in mind but I didn't know about specialization feature.
– Kevin Del Castillo Ramirez
Jul 2 at 19:01
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
play.rust-lang.org/… ?
– Stargateur
Jul 2 at 17:41