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::<F, I, S, B>::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::<F, I, S, B>::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
相关产品推荐
相关产品推荐

