Rust /rocket передает переменную конечным точкам

#rust #rust-rocket

#Ржавчина #rust-ракета

Вопрос:

Не в моих предпочтениях, но сегодня я вынужден написать немного 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(amp;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!("{:?}", amp;*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())
}
  

Есть какой-либо способ, eng доступный внутри sink метода конечной точки?

Комментарии:

1. Я не привык к Rocket, но во многих других фреймворках есть какой-то способ передать одно и то же состояние всем маршрутам. Я вижу, что у Rocket, вероятно, тоже есть это

2. Я получаю ту же ошибку, используя состояния.

Ответ №1:

Rc не является потокобезопасным, даже за мьютексом. Похоже, что Rc используется в eng.blocker.pool.pool , который является lifeguard::Pool . Итак, Engine не является потокобезопасным (по крайней мере, по умолчанию).

К счастью, похоже, что в adblock crate есть функция, называемая «объединение объектов», которая обеспечивает эту конкретную функциональность. Удаление этой функции (надеюсь) сделает ее потокобезопасной.

Ответ №2:

Rocket действительно упрощает совместное использование ресурсов между маршрутами (а также между main или любым другим потоком, из которого вы могли появиться main ). Они вызывают свой механизм state . Ознакомьтесь с его документацией здесь.

Чтобы привести краткий пример того, как это работает: вы создаете свой тип, которым хотите поделиться в своем приложении, и manage экземпляр этого типа в экземпляре rocket , который вы используете для своего приложения. В руководстве они приводят этот пример:

 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: amp;State<HitCount>) -> String {
    let current_count = hit_count.count.load(Ordering::Relaxed);
    format!("Number of visits: {}", current_count)
}
  

Пока я изучал rocket, мне нужно было поделиться структурой, которая содержала String , которая сама по себе не является потокобезопасной. Это означает, что вам нужно обернуть ее в Mutex прежде чем вы сможете manage использовать rocket.

Также, насколько я понимаю, только с одним ресурсом любого определенного типа может быть предоставлен общий доступ manage . Но в этом случае вы можете просто создать типы оболочек с другими именами и обойти это ограничение.