Rust/rocket 将变量传递给端点
Rust/rocket pass variable to endpoints
不符合我的喜好,但我今天不得不写一些 Rust,所以我试图创建一个只有一个端点的 Rocket 实例,但是,在那个端点上,我需要访问一个在 main 期间创建的变量.该变量需要很长时间才能实例化,所以这就是我在那里这样做的原因。
我的问题是我找不到安全通过它的方法。无论我做什么,编译器都会抱怨线程安全,即使库看起来是线程安全的:https://github.com/brave/adblock-rust/pull/130(在我的本地实例上找到提交的代码)
这是我得到的错误:
|
18 | / lazy_static! {
19 | | static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
20 | | }
| |_^ `std::rc::Rc<std::cell::RefCell<lifeguard::CappedCollection<std::vec::Vec<u64>>>>` cannot be sent between threads safely
|
...这是我的代码:
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use lazy_static::lazy_static;
use std::sync::Mutex;
use adblock::engine::Engine;
use adblock::lists::FilterFormat;
use rocket::request::{Form, FormError, FormDataError};
lazy_static! {
static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
}
fn main() {
if !Path::new("./rules.txt").exists() {
println!("rules file does not exist")
} else {
println!("loading rules");
let mut rules = vec![];
if let Ok(lines) = read_lines("./rules.txt") {
for line in lines {
if let Ok(ip) = line {
rules.insert(0, ip)
}
}
let eng = Engine::from_rules(&rules, FilterFormat::Standard);
rules_engine.lock().unwrap().push(eng);
rocket().launch();
}
}
}
#[derive(Debug, FromForm)]
struct FormInput<> {
#[form(field = "textarea")]
text_area: String
}
#[post("/", data = "<sink>")]
fn sink(sink: Result<Form<FormInput>, FormError>) -> String {
match sink {
Ok(form) => {
format!("{:?}", &*form)
}
Err(FormDataError::Io(_)) => format!("Form input was invalid UTF-8."),
Err(FormDataError::Malformed(f)) | Err(FormDataError::Parse(_, f)) => {
format!("Invalid form input: {}", f)
}
}
}
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![sink])
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
有什么方法可以在 sink
端点方法中使用 eng
?
Rc
不是线程安全的,即使在互斥量之后也是如此。看起来 Rc
在 eng.blocker.pool.pool
中使用了 lifeguard::Pool
。所以不,Engine
不是线程安全的(至少默认情况下)。
幸运的是,adblock crate 似乎有一个名为“object-pooling”的 feature,它启用了该特定功能。删除该功能将(希望)使其成为线程安全的。
Rocket 使得在路由之间共享资源变得非常容易(以及在 main
或您可能从 main
产生的任何其他线程之间)。他们称他们的机制为 state
。查看其文档 here.
举一个简短的例子来说明它是如何工作的:
您创建要在您的应用程序中共享的类型,并在您的应用程序使用的 rocket
实例中创建该类型的实例 manage
。在指南中,他们给出了这个例子:
use std::sync::atomic::AtomicUsize;
struct HitCount {
count: AtomicUsize
}
rocket::build().manage(HitCount { count: AtomicUsize::new(0) });
在路由中,您可以像这样访问资源(同样来自指南):
use rocket::State;
#[get("/count")]
fn count(hit_count: &State<HitCount>) -> String {
let current_count = hit_count.count.load(Ordering::Relaxed);
format!("Number of visits: {}", current_count)
}
虽然我学习了 rocket,但我需要共享一个包含 String
的结构,这本身不是线程安全的。这意味着您需要先将其包装成 Mutex
,然后才能使用火箭 manage
。
此外,据我了解,任何特定类型的资源只能与 manage
共享。但是您可以在这种情况下创建不同名称的包装器类型并解决该限制。
不符合我的喜好,但我今天不得不写一些 Rust,所以我试图创建一个只有一个端点的 Rocket 实例,但是,在那个端点上,我需要访问一个在 main 期间创建的变量.该变量需要很长时间才能实例化,所以这就是我在那里这样做的原因。
我的问题是我找不到安全通过它的方法。无论我做什么,编译器都会抱怨线程安全,即使库看起来是线程安全的:https://github.com/brave/adblock-rust/pull/130(在我的本地实例上找到提交的代码)
这是我得到的错误:
|
18 | / lazy_static! {
19 | | static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
20 | | }
| |_^ `std::rc::Rc<std::cell::RefCell<lifeguard::CappedCollection<std::vec::Vec<u64>>>>` cannot be sent between threads safely
|
...这是我的代码:
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use lazy_static::lazy_static;
use std::sync::Mutex;
use adblock::engine::Engine;
use adblock::lists::FilterFormat;
use rocket::request::{Form, FormError, FormDataError};
lazy_static! {
static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
}
fn main() {
if !Path::new("./rules.txt").exists() {
println!("rules file does not exist")
} else {
println!("loading rules");
let mut rules = vec![];
if let Ok(lines) = read_lines("./rules.txt") {
for line in lines {
if let Ok(ip) = line {
rules.insert(0, ip)
}
}
let eng = Engine::from_rules(&rules, FilterFormat::Standard);
rules_engine.lock().unwrap().push(eng);
rocket().launch();
}
}
}
#[derive(Debug, FromForm)]
struct FormInput<> {
#[form(field = "textarea")]
text_area: String
}
#[post("/", data = "<sink>")]
fn sink(sink: Result<Form<FormInput>, FormError>) -> String {
match sink {
Ok(form) => {
format!("{:?}", &*form)
}
Err(FormDataError::Io(_)) => format!("Form input was invalid UTF-8."),
Err(FormDataError::Malformed(f)) | Err(FormDataError::Parse(_, f)) => {
format!("Invalid form input: {}", f)
}
}
}
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![sink])
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
有什么方法可以在 sink
端点方法中使用 eng
?
Rc
不是线程安全的,即使在互斥量之后也是如此。看起来 Rc
在 eng.blocker.pool.pool
中使用了 lifeguard::Pool
。所以不,Engine
不是线程安全的(至少默认情况下)。
幸运的是,adblock crate 似乎有一个名为“object-pooling”的 feature,它启用了该特定功能。删除该功能将(希望)使其成为线程安全的。
Rocket 使得在路由之间共享资源变得非常容易(以及在 main
或您可能从 main
产生的任何其他线程之间)。他们称他们的机制为 state
。查看其文档 here.
举一个简短的例子来说明它是如何工作的:
您创建要在您的应用程序中共享的类型,并在您的应用程序使用的 rocket
实例中创建该类型的实例 manage
。在指南中,他们给出了这个例子:
use std::sync::atomic::AtomicUsize;
struct HitCount {
count: AtomicUsize
}
rocket::build().manage(HitCount { count: AtomicUsize::new(0) });
在路由中,您可以像这样访问资源(同样来自指南):
use rocket::State;
#[get("/count")]
fn count(hit_count: &State<HitCount>) -> String {
let current_count = hit_count.count.load(Ordering::Relaxed);
format!("Number of visits: {}", current_count)
}
虽然我学习了 rocket,但我需要共享一个包含 String
的结构,这本身不是线程安全的。这意味着您需要先将其包装成 Mutex
,然后才能使用火箭 manage
。
此外,据我了解,任何特定类型的资源只能与 manage
共享。但是您可以在这种情况下创建不同名称的包装器类型并解决该限制。