可以实现返回引用或拥有值的Trait方法

皮奥罗

我正在尝试使用一种可以实现返回引用或拥有值的方法来定义特征。

就像是:

struct Type;
trait Trait {
    type Value;
    fn f(&self) -> Self::Value;
}
impl Trait for () {
    type Value = Type;
    fn f(&self) -> Self::Value {
        Type
    }
}
impl Trait for (Type,) {
    type Value = &Type; // error[E0106]: missing lifetime specifier
    fn f(&self) -> Self::Value {
        &self.0
    }
}

但是这段代码不起作用,因为&Type缺少生存期说明符。我希望&Type拥有与&self(即fn f<'a>(&'a self) -> &'a Type相同的生命,但是我不知道如何在Rust中表达这一点。

我设法找到了两种方法来使此代码起作用,但我都不喜欢其中一种:

  1. 为特征本身添加明确的生命周期:

    trait Trait<'a> {
        type Value;
        fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
    }
    impl<'a> Trait<'a> for () {
        type Value = Type;
        fn f<'b>(&'b self) -> Self::Value
            where 'b: 'a
        {
            Type
        }
    }
    impl<'a> Trait<'a> for (Type,) {
        type Value = &'a Type;
        fn f<'b>(&'b self) -> Self::Value
            where 'b: 'a
        {
            &self.0
        }
    }
    

    我对这种解决方案不满意的是,任何使用都Trait需要明确的生命周期(我认为这本质上是不必要的),而且该特征的实现似乎不必要地复杂。

  2. 返回可能是参考的东西,例如std::borrow::Cow

    trait Trait {
        type Value;
        fn f<'a>(&'a self) -> Cow<'a, Self::Value>;
    }
    impl Trait for () {
        type Value = Type;
        fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
            Cow::Owned(Type)
        }
    }
    impl Trait for (Type,) {
        type Value = Type;
        fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
            Cow::Borrowed(&self.0)
        }
    }
    

    我不喜欢这种解决方案是().f()Cow<_>:我需要调用().f().into_owned()获得我的Type这似乎是不必要的(当Trait用作特征对象时,可能会导致一些可忽略的运行时开销)。

    还要注意,这Cow不是很好,因为它需要Self::Value实现ToOwned(实际上是Clone),这太过严格了。无论如何,在Cow没有此类约束的情况下实现替代方案很容易

还有其他解决方案吗?什么是标准/最常见/首选的?

肯尼

可以使用附加的关联对象在返回类型还是引用之间进行选择,再加上一些元编程魔术来解决此问题。

首先,一些帮助程序类型:

struct Value;
struct Reference;

trait ReturnKind<'a, T: ?Sized + 'a> {
    type Type: ?Sized;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Value {
    type Type = T;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Reference {
    type Type = &'a T;
}

ReturnKind是一个“类型级功能”,它返回T时“输入”是Value,并且&TReference

然后是特征:

trait Trait {
    type Value;
    type Return: for<'a> ReturnKind<'a, Self::Value>;

    fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, Self::Value>>::Type;
}

我们通过“调用”类型级别的函数来产生返回类型ReturnKind

“输入参数”Return需要实现特征以允许我们编写<Return as ReturnKind<'a, Value>>尽管我们不知道Self的确切寿命,但是我们可以使用HRTBReturn限制所有可能的寿命 Return: for<'a> ReturnKind<'a, Value>

用法:

impl Trait for () {
    type Value = f64;
    type Return = Value;

    fn f(&self) -> f64 {
        42.0
    }
}

impl Trait for (f64,) {
    type Value = f64;
    type Return = Reference;

    fn f(&self) -> &f64 {
        &self.0
    }
}

fn main() {
    let a: (f64,) = ( ().f(), );
    let b: &f64 = a.f();
    println!("{:?} {:?}", a, b);
    // (42,) 42
}

请注意,上述方法仅在Value类型具有'static生存期时才有效如果Value本身的寿命有限,则必须由知道此寿命Trait由于Rust还不支持关联的生存期,因此Trait<'foo>不幸的是,它必须像这样使用

struct Value;
struct Reference;
struct ExternalReference;

trait ReturnKind<'a, 's, T: ?Sized + 'a + 's> {
    type Type: ?Sized;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Value {
    type Type = T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Reference {
    type Type = &'a T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for ExternalReference {
    type Type = &'s T;
}

trait Trait<'s> {
    type Value: 's;
    type Return: for<'a> ReturnKind<'a, 's, Self::Value>;

    fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, 's, Self::Value>>::Type;
}

impl Trait<'static> for () {
    type Value = f64;
    type Return = Value;

    fn f(&self) -> f64 {
        42.0
    }
}

impl Trait<'static> for (f64,) {
    type Value = f64;
    type Return = Reference;

    fn f(&self) -> &f64 {
        &self.0
    }
}

impl<'a> Trait<'a> for (&'a f64,) {
    type Value = f64;
    type Return = ExternalReference;

    fn f(&self) -> &'a f64 {
        self.0
    }

}

fn main() {
    let a: (f64,) = ( ().f(), );
    let b: &f64 = a.f();
    let c: &f64 = (b,).f();
    println!("{:?} {:?} {:?}", a, b, c);
    // (42,) 42 42
}

但是,如果在特征上具有lifetime参数很好,那么OP已经提供了一个更简单的解决方案:

trait Trait<'a> {
    type Value;
    fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}

impl<'a> Trait<'a> for () {
    type Value = f64;
    fn f<'b: 'a>(&'b self) -> Self::Value {
        42.0
    }
}

impl<'a> Trait<'a> for (f64,) {
    type Value = &'a f64;
    fn f<'b: 'a>(&'b self) -> Self::Value {
        &self.0
    }
}
impl<'a, 's> Trait<'s> for (&'a f64,) {
    type Value = &'a f64;
    fn f<'b: 's>(&'b self) -> Self::Value {
        self.0
    }
}

fn main() {
    let a: (f64,) = ( ().f(), );
    let b: &f64 = a.f();
    let c: &f64 = (b,).f();
    println!("{:?} {:?} {:?}", a, b, c);
    // (42,) 42 42
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

为什么我可以返回对函数拥有值的引用?

实现一个 trait 方法,为拥有的类型返回一个有界的生命周期引用

从可以接受拥有或借用的值作为输入的特征方法中仅返回拥有的类型

返回引用当前函数拥有的数据的值

为什么“ impl Trait”返回值实现了“ Box <dyn Trait>”没有发送的功能?

从函数返回libusb :: Device的问题-尝试返回引用当前函数拥有的数据的值

Rust:返回一个引用当前函数拥有的数据的值

错误消息不清楚:返回引用当前函数拥有的数据的值

如何解决Rust中的“返回引用当前函数拥有的数据的值”错误?

我如何实现枚举的借用,拥有或取消引用?

使用帶有引用值的 Borrow trait

实现索引特征以返回不是引用的值

如何避免filter_map()在Rust中出现错误“返回引用当前函数拥有的数据的值”?

如何解决“返回引用当前函数拥有的数据的值”(结构之间的实际依赖关系)

是否可以在Rust中返回借用或拥有的类型?

重载带有返回值的模板推导类型的右值和左值引用及其实现

将可变的自引用传递给拥有对象的方法

为什么此函数返回(拥有的)值?

如何编写具有返回引用的方法并正确实现它的特征?

有什么方法可以在列表理解中返回值?

有类方法。我可以拥有班级属性吗?

有什么办法可以帮助代码返回值是const引用?

WeakReference.get() 在没有强引用后可以返回非空值吗?

方法可以返回错误或数组-错误的实现?

C#-如何拥有引用任何实现一组接口的对象的类型?

是否有一种惯用的方式来返回一个指针,该指针可以选择拥有其值

ReactJs 0.14-不变违规:addComponentAsRefTo(...):只有ReactOwner可以拥有引用

有没有一种方法可以强制执行引用左值?

Entry :: Occupied.get()返回一个值,该值引用当前函数拥有的数据,即使hashmap应该具有所有权