如何指定将作为闭包参数的关联类型的生命周期?

问题描述 投票:0回答:3

我有一个函数的特征,这个函数接受一个闭包作为参数,并且那个闭包需要一个参数,该参数需要是实现

Read
特征的某种类型:

trait CanRead {
    type Reader: io::Read;
    
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut Self::Reader);
}

通过指定

Read
并在
type Reader = Self;
函数中运行
fun(self);
,我可以轻松地为任何已经实现
do_reading
的东西实现此特征。

挑战是,我还想为某种必须创建

u8
的临时向量的类型实现此特征。那么关联类型
Reader
需要是引用类型,但我不知道要给它什么生命周期:

pub struct EmptyStruct { }

impl CanRead for EmptyStruct {
    type Reader = &[u8]; // doesn't compile; must specify a lifetime here
    
    fn do_reading<F>(&mut self, fun: F)
    where F: FnOnce(&mut Self::Reader) {
        let temp = vec![1, 2, 3];
        fun(&mut &temp[..]);
    }
}

我知道我需要指定一个生命周期,但是它会是什么?我查看了这个有用的相关问题,但是建议的方法都不起作用。问题是

Reader
类型的生命周期实际上与
EmptyStruct
实例的生命周期无关;相反,
Reader
引用类型只需要不超过对闭包本身的调用。有没有某种方法可以在 Rust 中指定这一点,或者有其他方法来解决这种模式?

playground 有我尝试过的方法,但没有成功。

(注意,我知道对于这个特定代码,

temp

向量可以替换为静态数组,但这不适用于我真正需要做的事情。)

rust lifetime
3个回答

0
投票
这是我当前的解决方法。我希望有人能给出比这更好的解决方案!

解决方法是使用动态分派,完全避免关联类型,如下所示:

pub trait CanRead { fn do_reading<F>(&mut self, fun: F) where F: FnOnce(&mut dyn io::Read); } pub struct EmptyStruct { } impl CanRead for EmptyStruct { fn do_reading<F>(&mut self, fun: F) where F: FnOnce(&mut dyn io::Read) { let temp = vec![1, 2, 3]; fun(&mut &temp[..]); } }
这里的缺点是,因为闭包的访问是通过

&mut dyn

引用进行的,所以你会因为
动态调度而损失一些运行时效率。当然,惩罚可能不会太大,但如果可能的话,最好使用 Rust 强大的类型系统和泛型来避免这种情况。


0
投票
自从提出这个问题以来,Rust 的进步现在使这个问题可以解决。您需要使用

通用关联类型

pub trait CanRead { type Reader<'a>: io::Read where Self: 'a; fn do_reading<F>(&mut self, fun: F) where for<'a> F: FnOnce(&mut Self::Reader<'a>); }
这里,关联类型

Reader<'a>

采用生命周期参数,使其成为通用的。有一个生命周期界限 
where Self: 'a
,说明参数的生命周期必须短于(或等于)实现 
CanRead
 的对象的生命周期(您可以将其读作“where 
Self
 outlives 
'a
”) );这个生命周期界限是泛型关联类型的标准,因为它通常是简单的真实(实现 
CanRead
 的类型的方法不太可能在没有该类型值的情况下尝试使用 
Reader
)并且使其更容易让编译器对程序进行推理。

既然

Reader

是通用的,
do_reading
现在可以指定
fun
必须能够与读者的
所有生命周期一起使用,而不仅仅是一个特定的生命周期:for<'a>
意味着“对于所有生命周期
'a
” ”,因此 
where
 上的 
do_reading
 子句可以读作“其中 
F
 是一个 
FnOnce
,可以处理对具有任意生命周期的 
Self::Reader
 的可变引用的参数”。 (好吧,所以 
do_reading
 可能需要 
Self::Reader
 的生命周期至少持续到 
do_reading
 调用的主体,但 Rust 会自动将声明解释为包含该要求。)

有了

CanRead

 的定义,就可以像这样实现 
do_reading

impl CanRead for EmptyStruct { type Reader<'a> = &'a [u8] where Self: 'a; fn do_reading<F>(&mut self, fun: F) where for<'a> F: FnOnce(&mut Self::Reader<'a>) { let temp = vec![1, 2, 3]; fun(&mut &temp[..]); } }
这与你在操场上的基本相同 - 我必须更改 

Reader

 的定义和 
do_reading
 的签名以匹配该特征,但“明显”的代码在函数体内可以正常工作。 

© www.soinside.com 2019 - 2024. All rights reserved.