我正在尝试使用一种可以实现返回引用或拥有值的方法来定义特征。
就像是:
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中表达这一点。
我设法找到了两种方法来使此代码起作用,但我都不喜欢其中一种:
为特征本身添加明确的生命周期:
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
需要明确的生命周期(我认为这本质上是不必要的),而且该特征的实现似乎不必要地复杂。
返回可能是参考的东西,例如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
,并且&T
对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
。
“输入参数”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] 删除。
我来说两句