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

Rust Ntex服务器配置中无clone/全局变量传递结构体向量的方法

在ntex中无需clone或全局变量传递结构体配置的方法

我想在使用ntex搭建HTTP服务器时,不通过clone()深克隆结构体,也不使用全局变量,就能在服务器配置中传递结构体类型的变量。

以下是我的示例代码:

use ntex::web::{self, App, HttpServer};

struct HV {
    name: String,
}

struct HS {
    hosts: Vec<HV>,
    sock_addr: Option<String>,
}

struct MS {
    servers: Vec<HS>,
}

impl MS {
    fn new() -> Self {
        MS {
            servers: Vec::new(),
        }
    }
}

async fn index() -> impl web::Responder {
    "Hello World! (index)"
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
    let mut ms = MS::new();
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.com".to_string() },
            HV { name: "users.example.com".to_string() },
        ],
        sock_addr: Some("localhost:8080".to_string()),
    });
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.net".to_string() },
            HV { name: "users.example.net".to_string() },
        ],
        sock_addr: Some("localhost:8081".to_string()),
    });

    for server in ms.servers.iter_mut() {
        if let Some(sock_addr) = &server.sock_addr {
            println!("Socket Address : {}", sock_addr);

            HttpServer::new(move || {
                let mut app = App::new();
                for host in server.hosts.iter_mut() {
                    println!("Host Name : {}", host.name);
                    let scope = web::scope("/")
                        .guard(web::guard::Host(&host.name))
                        .route("/", web::get().to(index));

                    app = app.service(scope);
                }

                app
            })
            .bind(sock_addr)?
            .run()
            .await?;
        } else {
            println!("Sock Addr Not Found");
        }
    }

    Ok(())
}

运行后出现如下错误:

error[E0277]: the trait bound `&mut HS: Clone` is not satisfied in `{closure@src/main.rs:54:29: 54:36}`
  --> src/main.rs:54:29
   |
54 |               HttpServer::new(move || {
   |               --------------- ^------
   |               |               |
   |  _____________|_______________within this `{closure@src/main.rs:54:29: 54:36}`
   | |             |
   | |             required by a bound introduced by this call
55 | |                 let mut app = App::new();
56 | |                 for host in server.hosts.iter_mut() {
57 | |                     println!("Host Name : {}", host.name);
...  |
65 | |                 app
66 | |             })
   | |_____________^ within `{closure@src/main.rs:54:29: 54:36}`, the trait `Clone` is not implemented for `&mut HS`, which is required by `{closure@src/main.rs:54:29: 54:36}: Clone`
   |
   = note: `Clone` is implemented for `&HS`, but not for `&mut HS`
note: required because it's used within this closure
  --> src/main.rs:54:29
   |
54 |             HttpServer::new(move || {
   |                             ^^^^^^^
note: required by a bound in `HttpServer::&lt;F, I, S, B&gt;::new`
  --> /home/explorer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ntex-1.1.2/src/web/server.rs:87:27
   |
87 |     F: Fn() -> I + Send + Clone + 'static,
   |                           ^^^^^ required by this bound in `HttpServer::&lt;F, I, S, B&gt;::new`
...
96 |     pub fn new(factory: F) -> Self {
   |            --- required by a bound in this associated function

解决方法

错误根源是HttpServer::new要求传入的工厂闭包必须实现Clone(因为ntex会为每个工作线程克隆闭包实例),而你捕获的&mut HS(可变引用)并没有实现Clone。以下两种方法可以满足你的需求:

方法一:提前提取所需最小数据

直接在创建服务器前,提取闭包需要的核心数据(比如主机名字符串列表),避免捕获整个结构体。由于Vec<String>实现了Clone,闭包可以满足约束,同时不需要深克隆原结构体:

use ntex::web::{self, App, HttpServer};

struct HV {
    name: String,
}

struct HS {
    hosts: Vec<HV>,
    sock_addr: Option<String>,
}

struct MS {
    servers: Vec<HS>,
}

impl MS {
    fn new() -> Self {
        MS {
            servers: Vec::new(),
        }
    }
}

async fn index() -> impl web::Responder {
    "Hello World! (index)"
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
    let mut ms = MS::new();
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.com".to_string() },
            HV { name: "users.example.com".to_string() },
        ],
        sock_addr: Some("localhost:8080".to_string()),
    });
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.net".to_string() },
            HV { name: "users.example.net".to_string() },
        ],
        sock_addr: Some("localhost:8081".to_string()),
    });

    for server in ms.servers.into_iter() {
        if let Some(sock_addr) = server.sock_addr {
            println!("Socket Address : {}", sock_addr);
            // 提前提取主机名字符串列表
            let host_names: Vec<String> = server.hosts.into_iter().map(|hv| hv.name).collect();

            HttpServer::new(move || {
                let mut app = App::new();
                for host in &host_names {
                    println!("Host Name : {}", host);
                    let scope = web::scope("/")
                        .guard(web::guard::Host(host))
                        .route("/", web::get().to(index));

                    app = app.service(scope);
                }

                app
            })
            .bind(sock_addr)?
            .run()
            .await?;
        } else {
            println!("Sock Addr Not Found");
        }
    }

    Ok(())
}

方法二:用Arc共享结构体(无深克隆)

使用Arc(原子引用计数指针)共享结构体实例,Arc::clone只是增加引用计数,属于轻量操作,不会深克隆整个结构体:

use ntex::web::{self, App, HttpServer};
use std::sync::Arc;

struct HV {
    name: String,
}

struct HS {
    hosts: Vec<HV>,
    sock_addr: Option<String>,
}

struct MS {
    servers: Vec<HS>,
}

impl MS {
    fn new() -> Self {
        MS {
            servers: Vec::new(),
        }
    }
}

async fn index() -> impl web::Responder {
    "Hello World! (index)"
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
    let mut ms = MS::new();
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.com".to_string() },
            HV { name: "users.example.com".to_string() },
        ],
        sock_addr: Some("localhost:8080".to_string()),
    });
    ms.servers.push(HS {
        hosts: vec![
            HV { name: "example.net".to_string() },
            HV { name: "users.example.net".to_string() },
        ],
        sock_addr: Some("localhost:8081".to_string()),
    });

    for server in ms.servers.into_iter() {
        if let Some(sock_addr) = server.sock_addr {
            println!("Socket Address : {}", sock_addr);
            // 用Arc包裹结构体
            let server = Arc::new(server);

            HttpServer::new(move || {
                // 轻量克隆Arc,仅增加引用计数
                let server = Arc::clone(&server);
                let mut app = App::new();
                for host in &server.hosts {
                    println!("Host Name : {}", host.name);
                    let scope = web::scope("/")
                        .guard(web::guard::Host(&host.name))
                        .route("/", web::get().to(index));

                    app = app.service(scope);
                }

                app
            })
            .bind(sock_addr)?
            .run()
            .await?;
        } else {
            println!("Sock Addr Not Found");
        }
    }

    Ok(())
}

这两种方法都不需要全局变量,也避免了对原结构体的深克隆操作,符合你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 08:50:56