【问题标题】:Conditional trait bounds possible?条件性状界限可能吗?
【发布时间】:2021-03-17 09:04:10
【问题描述】:

baz 中的 trait 绑定在 flag=true 时是不必要的强,在这种情况下我们只需要 Foo

我希望baz 可以接受where T: Foo 并且flag=false 时强制执行Bar 绑定。

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);
    }
}

将绑定更改为where T: Foo 当然不会编译:

bar(t)

.........^ trait Bar 没有为T 实现

引入一个可以被!Bar 类型调用的新函数quux 可能是我必须接受的解决方案。 但是Bar!Bar 类型有什么办法都可以访问单个函数baz

如果flag=falseT: !Bar 是可接受的,则涉及运行时恐慌的解决方案。

【问题讨论】:

  • 我看不出把它分成两个函数是如何“破坏 API”的。如果 baz() 函数已经存在并带有 Foo 特征绑定,则添加带有 Bar 绑定的新函数不应破坏任何现有代码。
  • 事实上它以Bar trait bound 的形式存在,我刚刚粘贴了无法编译的trait bound 的Foo 形式。
  • 所以您想松开绑定在baz 上的特征,从当前的Bar 到问题中显示的Foo(这不会编译)? “打破” API 是指将其分解为两个函数,而不是像进行向后不兼容的更改那样打破?
  • 是的。我会编辑这个问题,希望这会变得更清楚。
  • 感谢您的编辑。我认为你所要求的在当前的 Rust 中是不可能的,但通过专业化是可能的。专业化可能允许您定义一个新特征并实现它来为T: Foo 做一件事,为T: Bar 做另一件事(由于重叠,目前不允许这样做)。然后baz 会从该特征中调用该方法,这将解决正确的问题。

标签: rust traits


【解决方案1】:

我相信你所要求的在当前的 Rust 中是不可能的,因为它需要 specialization。通过RFC 提出的专业化,baz 可以使用辅助特征来实现,其中T: Foo 的全面实现和T: Bar 的专门实现。

遗憾的是,此时专业化似乎不是优先事项(被 const 泛型等更重要的特性所取代),因此它需要一段时间才能实现和稳定。不过,为了好玩,这里有一个基于当前夜间发现的专业化的实现:

#![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");
}

playground中的可编译代码。)

【讨论】:

    猜你喜欢
    • 2010-12-21
    • 2016-06-24
    • 1970-01-01
    • 1970-01-01
    • 2019-06-26
    • 2012-09-17
    • 2016-06-11
    • 1970-01-01
    • 2011-02-20
    相关资源
    最近更新 更多