Невозможно заимствовать в Rc как изменяемый

Прежде всего, я новичок в Rust :-)

Проблема: я хочу создать модуль RestServer, содержащий методы (actix-web) для добавления маршрутов и запуска сервера.

struct Route
{
   url: String,
   request: String,
   handler: Box<dyn Fn(HttpRequest) -> HttpResponse>
}


impl PartialEq for Route {
   fn eq(&self, other: &Self) -> bool {
     self.url == other.url
   }
}

impl Eq for Route {}

impl Hash for Route {
   fn hash<H: Hasher>(&self, hasher: &mut H) {
      self.url.hash(hasher);
   }
}

это структура маршрута, эта структура содержит URL-адрес маршрута, тип запроса (GET, POST и т. д.), а hanlder - это функция, которая должна перехватывать запрос и возвращать HTTPResponse

pub struct RestServer
{
   scopes: HashMap<String, Rc<HashSet<Route>>>,
   routes: HashSet<Route>,
   host: String,
}

impl RestServer {

   pub fn add_route(self, req: &str, funct: impl Fn(HttpRequest) -> HttpResponse + 'static,
                 route: &str, scope: Option<&str>) -> RestServer
   {
       let mut routes_end = self.routes;
       let mut scopes_end = self.scopes;
       let url = self.host;
       let route = Route {
          url: String::from(route),
          request: String::from(req),
          handler: Box::new(funct)
    };

    if let Some(x) = scope {
        if let Some(y) = scopes_end.get(x) {
            let mut cloned_y = Rc::clone(y);
            cloned_y.insert(route);
            scopes_end.insert(String::from(x), cloned_y);
        }else {
            let mut hash_scopes = HashSet::new();
            hash_scopes.insert(route);
            scopes_end.insert(String::from(x), Rc::new(hash_scopes));
        }
    } else {
        routes_end.insert(route);
    }

    RestServer {
        scopes: scopes_end,
        routes: routes_end,
        host: String::from(url)
    }
  }

последний код - это реализация RestServer. Наиболее важной частью является функция add_route, эта функция получает в качестве параметров маршрут, который является строкой, обработчиком функции, строкой запроса и областью действия. Сначала я создаю объект маршрута. Я проверяю, существует ли область в HashMap, если да, мне нужно взять фактическую область и обновить HashSet.

Когда я создаю код, я получаю следующую ошибку

   error[E0596]: cannot borrow data in an `Rc` as mutable
   --> interface/src/rest/mod.rs:60:17
   |
60 |                 cloned_y.insert(route);
   |                 ^^^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not 
     implemented for `std::rc::Rc<std::collections::HashSet<rest::Route>>`

Я знаю, что компилятор мне немного помогает, но, честно говоря, я понятия не имею, как это сделать и могу ли я сделать какое-нибудь простое решение. После большого поиска в google я нашел решение в RefCell, но не так уж и понятно

Заранее спасибо за помощь


person Andrea Mucci    schedule 28.10.2019    source источник


Ответы (1)


Вы не можете заимствовать указатель подсчета ссылок как изменяемый; это потому, что одна из гарантий, которые он предоставляет, возможна только в том случае, если структура доступна только для чтения.

Однако вы можете обойти это, но это потребует некоторых изменений подписи.

Введите внутреннюю изменчивость

Внутренняя изменчивость - это концепция, которую вы, возможно, знаете из других языков программирования в форме мьютексов, атомики и примитивов синхронизации. На практике эти структуры позволяют вам временно гарантировать, что вы являетесь единственным средством доступа к данной переменной.

В Rust это особенно хорошо, так как позволяет нам извлекать изменяемую ссылку на внутренний член из структуры, для работы которой требуются только неизменяемые ссылки на себя. Идеально подходит для Rc.

В зависимости от того, что вам нужно для ваших нужд, вы найдете _2 _ и RefCell, чтобы они были именно тем, что вам нужно для этого. Они не являются потокобезопасными, но, опять же, и Rc, так что это не совсем повод для прерывания сделки.

На практике это работает очень просто:

let data = Rc::new(RefCell::new(true));
{
  let mut reference = data.borrow_mut();
  *reference = false;
}
println!("{:?}", data);

детская площадка

(Если вам когда-нибудь понадобятся многопоточные версии, Arc заменяет Rc и Mutex или RwLock заменяет _10 _ / _ 11_)

person Sébastien Renauld    schedule 29.10.2019
comment
Большое спасибо за вашу помощь, я буду больше всего обновлять решение в ближайшие дни :-) - person Andrea Mucci; 30.10.2019