Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional trait bounds possible?

Tags:

rust

traits

The trait bound in baz is unnecessarily strong for when flag=true, in which case we only require Foo.

I would like to have that baz can accept where T: Foo and only enforce the Bar bound when flag=false.

trait Foo {}
trait Bar: Foo {}

fn foo<T>(t: T) where T: Foo {}
fn bar<T>(t: T) where T: Bar {}

fn baz<T>(t: T, flag: bool) 
where T: Bar 
{
    if flag {
        foo(t);
    } else {
        bar(t);
    }
}

Changing the bound to where T: Foo will of course not compile:

bar(t)

.........^ the trait Bar is not implemented for T

Introducing a new function quux which can be called by !Bar types is probably the solution I'll have to accept. But is there any way both Bar and !Bar types could both access a single function baz?

A solution which involves a runtime panic if flag=false and T: !Bar is acceptable.

like image 365
sphere Avatar asked Feb 17 '26 00:02

sphere


1 Answers

I believe what you are asking for is not possible in current Rust, as it requires specialization. Unfortunately specialization as proposed is very unlikely to be finished and stabilized, due to multiple soundness issues.

With that in mind, here how baz could be implemented using those proposals - it would use a helper trait with a blanket implementation for T: Foo and a specialized implementation for T: Bar:

#![feature(specialization)]

trait Foo {}
trait Bar: Foo {}

fn foo<T: Foo>(_t: T) -> &'static str {
    "foo"
}
fn bar<T: Bar>(_t: T) -> &'static str {
    "bar"
}

trait Dispatch {
    fn dispatch(self, flag: bool) -> &'static str;
}

impl<T: Foo> Dispatch for T {
    default fn dispatch(self, flag: bool) -> &'static str {
        // there is no way to call bar(self) here, so we can only assert the flag is true
        assert!(flag);
        foo(self)
    }
}

impl<T: Bar> Dispatch for T {
    fn dispatch(self, flag: bool) -> &'static str {
        if flag {
            foo(self)
        } else {
            bar(self)
        }
    }
}

fn baz<T: Foo>(t: T, flag: bool) -> &'static str {
    t.dispatch(flag)
}

fn main() {
    struct A;
    impl Foo for A {}
    assert_eq!(baz(A, true), "foo");
    //baz(A, false) panics

    struct B;
    impl Foo for B {}
    impl Bar for B {}
    assert_eq!(baz(B, true), "foo");
    assert_eq!(baz(B, false), "bar");
}

(Compilable code in the playground.)

like image 168
user4815162342 Avatar answered Feb 19 '26 15:02

user4815162342