You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建持有构造函数自有变量引用的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)
    }
}

说明

  1. 结构体现在拥有conn和sock的所有权,它们的生命周期与TlsSocket实例一致,不会在函数结束时被销毁。
  2. get_stream方法返回的Stream借用了结构体内部的对象,生命周期被Rust自动推导为与&mut self一致,完全符合安全规则。
  3. 当需要进行TLS读写操作时,只需调用get_stream()即可获得可用的Stream实例。

内容的提问来源于stack exchange,提问作者qnilab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:13:16