可以实现以返回引用或拥有的值的特征方法 [英] Trait method that can be implemented to either return a reference or an owned value

查看:63
本文介绍了可以实现以返回引用或拥有的值的特征方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to define a trait with a method that can be implemented to either return a reference or an owned value.

类似于:

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 中表达.

This piece of code doesn't work though, since &Type is missing a lifetime specifier. I'd want &Type to have the same lifetime as &self (i.e. fn f<'a>(&'a self) -> &'a Type), but I don't know how to express this in Rust.

我设法找到了几种方法来使这段代码工作,但我不喜欢它们中的任何一种:

I managed to find a couple of ways to make this code work, but I don't love either of them:

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

  1. Adding an explicit lifetime to the trait itself:

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 的东西都需要一个明确的生命周期(我认为这不是本质上必要的),而且这个 trait 实现起来似乎不必要地复杂.

What I don't like of this solution is that anything using Trait needs an explicit lifetime (which I believe is not intrinsically necessary), plus the trait seems unnecessarily complicated to implement.

返回一些可能是也可能不是参考的东西 - 比如 std::borrow::Cow:

Returning something that might or might not be a reference - like 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 作为 trait 对象时可能会导致一些可以忽略不计的运行时开销).

What I don't like of this solution is that ().f() is a Cow<_>: I'd need to call ().f().into_owned() to obtain my Type. That seems unnecessary (and might result in some negligible run-time overhead when using Trait as a trait object).

还要注意 Cow 不好,因为它要求 Self::Value 实现 ToOwned(因此,实际上,Clone),这个要求太强了.无论如何,在没有这些限制的情况下实现 Cow 的替代方案是很容易的.

Also note that Cow is not good since it requires that Self::Value implements ToOwned (thus, practically, Clone), which is too strong of a requirement. It's anyways easy to implement an alternative to Cow without such constraints.

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

Are there any other solutions to this problem? What's the standard/most common/preferred one?

推荐答案

这可以通过使用额外的关联对象来解决,以在返回类型或引用之间进行选择,再加上一些元编程魔法.

This could be solved using an additional associated object to choose between whether to return a type or a reference, plus some meta-programming magic.

首先,一些辅助类型:

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 是一个类型级函数",当输入"为 Value 时返回 T&参考的T.

ReturnKind is a "type-level function" which returns T when the "input" is Value, and &T for Reference.

然后是特征:

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来产生返回类型.

We produce the return type by "calling" the type-level function ReturnKind.

输入参数"Return 需要实现 trait 以允许我们编写 >.虽然我们不知道 Self 的生命周期究竟是什么,但我们可以使用 HRTB 返回:for<'a>ReturnKind<'a, Value>.

The "input argument" Return needs to implement the trait to allow us to write <Return as ReturnKind<'a, Value>>. Although we don't know what exactly the lifetime Self will be, we could make Return bound by all possible lifetime using HRTB 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> 一样使用:


Note that the above only works when the Value type has 'static lifetime. If the Value itself has a limited lifetime, this lifetime has to be known by the Trait. Since Rust doesn't support associated lifetimes yet, it has to be used like Trait<'foo>, unfortunately:

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
}

但是如果在特征上设置生命周期参数没问题,那么 OP 已经提供了一个更简单的解决方案:

But if having the lifetime parameter on the trait is fine, then OP already provided an easier solution:

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
}

这篇关于可以实现以返回引用或拥有的值的特征方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆