Получение строки запроса из объекта Window в WebAssembly в Rust - PullRequest
0 голосов
/ 02 мая 2020

Контекст: я изучаю Rust & WebAssembly и в качестве практического упражнения у меня есть проект, который рисует вещи в HTML Canvas из Rust кода. Я хочу получить строку запроса из веб-запроса, и оттуда код может решить, какую функцию рисования вызывать.

Я написал эту функцию, чтобы просто вернуть строку запроса с удаленным начальным ?:

fn decode_request(window: web_sys::Window) -> std::string::String {
    let document = window.document().expect("no global window exist");
    let location = document.location().expect("no location exists");
    let raw_search = location.search().expect("no search exists");
    let search_str = raw_search.trim_start_matches("?");
    format!("{}", search_str)
}

Это работает, но кажется удивительно многословным, учитывая, насколько проще было бы это на некоторых других языках, которые я использовал.

Есть ли более простой способ сделать это? Или многословие - это просто цена, которую вы платите за безопасность в Rust, и я должен просто привыкнуть к ней?

Редактировать за ответ от @ IInspectable : я попробовал подход с цепочкой и получил ошибка:

temporary value dropped while borrowed

creates a temporary which is freed while still in use

note: consider using a `let` binding to create a longer lived value rustc(E0716)

Было бы неплохо понять это лучше; Я все еще получаю тонкости владения через мою голову. Сейчас:

fn decode_request(window: Window) -> std::string::String {
    let location = window.location();
    let search_str = location.search().expect("no search exists");
    let search_str = search_str.trim_start_matches('?');
    search_str.to_owned()
}

, что, безусловно, является улучшением.

1 Ответ

1 голос
/ 03 мая 2020

Этот вопрос действительно касается дизайна API, а не его влияния на реализацию. Реализация оказалась довольно многословной в основном из-за выбранного контракта: либо выведите значение, либо d ie. В этом контракте нет ничего плохого. Клиент, вызывающий эту функцию, никогда не обнаружит недействительные данные, поэтому это совершенно безопасно.

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

Прежде чем перейти к изучению альтернатив, давайте переписываем исходный код более компактным способом, объединяя вызовы вместе, без явного присвоения каждого результата переменная:

fn decode_request(window: web_sys::Window) -> std::string::String {
    window
        .location()
        .search().expect("no search exists")
        .trim_start_matches('?')
        .to_owned()
}

Я не знаком с web_sys ящиком, так что здесь есть немного догадок. А именно, предположение, что window.location() возвращает то же значение, что и document() 's location(). Помимо цепочки вызовов, представленный код использует еще два изменения:

  • trim_start_matches() передается символьный литерал вместо строкового литерала. Это позволяет получить оптимальный код, не полагаясь на оптимизатор компилятора, чтобы выяснить, что строка длиной 1 пытается найти отдельный символ.
  • Возвращаемое значение создается путем вызова to_owned(). Макрос format! добавляет издержки и в итоге вызывает to_string(). Хотя в этом случае это будет демонстрировать то же поведение, использование семантически более точной функции to_owned() поможет вам обнаруживать ошибки во время компиляции (например, если вы случайно вернули 42.to_string()).

Альтернативы

Более естественным способом реализации этой функции является возвращение либо значения, представляющего строку запроса, либо вообще никакого значения. Rust предоставляет тип Option для удобного моделирования этого:

fn decode_request(window: web_sys::Window) -> Option<String> {
    match window
          .location()
          .search() {
        Ok(s) => Some(s.trim_start_matches('?').to_owned()),
        _     => None,
    }
}

Это позволяет клиенту функции принимать решения в зависимости от того, возвращает ли функция Some(s) или None. Это отображает все состояния ошибки в значение None.

Если желательно сообщить причину сбоя вызывающей стороне, функция decode_request может выбрать возврат Result* Вместо этого значение 1042 *, например Result<String, wasm_bindgen::JsValue>. При этом реализация может использовать оператор ?, чтобы компактно распространять ошибки вызывающей стороне:

fn decode_request(window: web_sys::Window) -> Result<String, wasm_bindgen::JsValue> {
    Ok(window
        .location()
        .search()?
        .trim_start_matches('?')
        .to_owned())
}
...