如何理解 Rustls 的 peer_certificates 返回类型中的 'static?
结论:这里的 ‘static 不要求证书对象永远存活
CertificateDer<'static> 可以持有运行时分配、由自身拥有的证书数据。这个值仍然可以随所属对象一起释放。
方法返回的外层引用也没有 'static 生命周期:
pub fn peer_certificates(&self) -> Option<&[CertificateDer<'static>]>
这里有两层生命周期:外层 &[...] 的借用期限,以及元素类型 CertificateDer<'static> 中的生命周期参数。
外层引用的生命周期由 &self 决定
按照 Rust 的生命周期省略规则,这个签名可以展开为:
pub fn peer_certificates<'a>(
&'a self,
) -> Option<&'a [CertificateDer<'static>]>
返回的证书切片借用了 self 中的数据,不能在所属对象销毁后继续使用。只要这份借用还需要使用,Rust 就会阻止销毁或以冲突方式修改被借用的数据。
下面的签名与原始方法不同:
pub fn peer_certificates(
&self,
) -> Option<&'static [CertificateDer<'static>]>
它要求外层切片引用在整个程序运行期间都有效,而原始方法没有这个要求。
CertificateDer<‘static> 可以持有动态分配的数据
CertificateDer 既支持借用数据,也支持拥有数据,并非只保存引用。
借用形式需要保证底层字节在借用期间有效。拥有形式由该值持有底层存储,因此不依赖局部变量提供的短期引用。
例如,使用 Rustls 的 pki_types 导出路径,可以这样表示两种情况:
use rustls::pki_types::CertificateDer;
// 借用调用者提供的字节,生命周期受 bytes 限制。
fn borrow_der<'a>(bytes: &'a [u8]) -> CertificateDer<'a> {
CertificateDer::from(bytes)
}
// 接管 Vec 的所有权,不再借用调用者的局部数据。
fn own_der(bytes: Vec<u8>) -> CertificateDer<'static> {
CertificateDer::from(bytes)
}
own_der 接收的 Vec<u8> 可以在运行时创建,也可以装载从网络接收的数据。调用后,所有权转移到返回值中;返回值被释放时,其拥有的字节存储也会被释放。
这些转换用于包装 DER 字节,不能据此认定字节已经通过证书解析或验证。
对这种类型而言,'static 表示它不依赖短于 'static 的外部借用。它既可能使用真正的静态字节,也可能拥有运行时分配的字节。仅凭这个类型参数,无法判断数据是否在编译时就已存在。
连接状态与生命周期约束是两个问题
连接建立前是否已有证书、握手过程中何时取得证书,属于运行时状态。返回类型中的 Option 允许方法在没有可返回的对端证书时返回 None。
连接断开后,证书存储也不一定立即失效。网络连接状态与保存证书的 Rust 对象可以有不同的存活时间。具体什么时候返回 Some 或 None、断开后是否保留证书,需要依据相应版本的 API 行为判断,单靠这个签名无法确定。
无论连接处于什么状态,只要方法返回了切片引用,Rust 的借用规则就要求它在使用期间保持有效。内部的 'static 不会绕过这条规则。
为什么不写成 CertificateDer<’_>?
'_ 对应另一层约束。若签名写成:
pub fn peer_certificates(&self) -> Option<&[CertificateDer<'_>]>
按这里的生命周期省略规则,可以理解为:
pub fn peer_certificates<'a>(
&'a self,
) -> Option<&'a [CertificateDer<'a>]>
这个签名允许内部证书数据也依赖期限为 'a 的借用。原始签名提供了更强的保证:证书元素内部没有这种短期借用依赖。
通过 peer_certificates() 拿到的仍然是借用切片,调用者不会因此自动获得证书的所有权。若要在所属连接对象销毁后继续保留证书数据,需要另外取得拥有所有权的数据。
备注:内容仅供参考。
