如何创建持有构造函数自有变量引用的Rust对象?
Rust中TlsSocket构造函数的生命周期错误分析与解决
问题代码
use std::net::TcpStream; use rustls::{ClientConnection, RootCertStore, Stream}; pub struct TlsSocket<'a> { tls_instance: Stream<'a, ClientConnection, TcpStream>, } impl<'a> TlsSocket<'a> { pub fn new() -> Self { let root_store = RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let mut config = rustls::ClientConfig::builder() .with_root_certificates(root_store) .with_no_client_auth(); // Allow using SSLKEYLOGFILE. config.key_log = Arc::new(rustls::KeyLogFile::new()); let server_name = "my_site.net".try_into().unwrap(); let mut conn = rustls::ClientConnection::new(Arc::new(config), server_name).unwrap(); let mut sock = TcpStream::connect("my_site.net:8000").unwrap(); let tls = rustls::Stream::new(&mut conn, &mut sock); TlsSocket { tls_instance: tls } } }
错误信息
cannot return value referencing local variable
sock
returns a value referencing data owned by the current function
错误原因
你对生命周期的理解存在偏差:rustls::Stream<'a, C, S>本质是一个引用包装器,它并没有持有ClientConnection和TcpStream的所有权,只是持有这两个对象的可变引用。
在new函数中,conn和sock是局部变量,函数执行完毕后会被Rust自动销毁。但你返回的TlsSocket中的Stream还持有对它们的引用,这就产生了悬垂引用——引用指向的数据已经不存在,Rust的安全机制会直接阻止这种情况发生。
生命周期注解'a只是告知编译器引用的生命周期关系,它无法改变数据的所有权归属,也不能延长局部变量的生命周期。
解决方案
正确的做法是让TlsSocket结构体直接持有ClientConnection和TcpStream的所有权,而非持有引用类型的Stream。需要读写时,再临时创建Stream来借用这两个对象:
use std::net::TcpStream; use std::sync::Arc; use rustls::{ClientConnection, RootCertStore, Stream}; pub struct TlsSocket { conn: ClientConnection, sock: TcpStream, } impl TlsSocket { pub fn new() -> Self { let root_store = RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let mut config = rustls::ClientConfig::builder() .with_root_certificates(root_store) .with_no_client_auth(); // 启用SSLKEYLOGFILE支持 config.key_log = Arc::new(rustls::KeyLogFile::new()); let server_name = "my_site.net".try_into().unwrap(); let conn = rustls::ClientConnection::new(Arc::new(config), server_name).unwrap(); let sock = TcpStream::connect("my_site.net:8000").unwrap(); TlsSocket { conn, sock } } // 获取用于读写的Stream,它会借用结构体中的conn和sock pub fn get_stream(&mut self) -> Stream<'_, ClientConnection, TcpStream> { Stream::new(&mut self.conn, &mut self.sock) } }
说明
- 结构体现在拥有
conn和sock的所有权,它们的生命周期与TlsSocket实例一致,不会在函数结束时被销毁。 get_stream方法返回的Stream借用了结构体内部的对象,生命周期被Rust自动推导为与&mut self一致,完全符合安全规则。- 当需要进行TLS读写操作时,只需调用
get_stream()即可获得可用的Stream实例。
内容的提问来源于stack exchange,提问作者qnilab
相关产品推荐
相关产品推荐

