在 Rust 的 WebAssembly 中从 Window 对象获取查询字符串

Getting query string from Window object in WebAssembly in Rust

上下文:我正在学习 Rust 和 WebAssembly,作为练习,我有一个项目可以从 Rust 代码绘制 HTML Canvas 中的东西。我想从网络请求中获取查询字符串,代码可以从那里决定调用哪个绘图函数。

我将此函数写入 return 删除了前导 ? 的查询字符串:

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()
}

这当然是一个进步。

这个问题实际上是关于 API 设计而不是它对实现的影响。实施结果相当冗长,主要是由于选择的合同:要么产生价值,要么消亡。这份合同本身并没有错。调用此函数的客户端永远不会观察到无效数据,因此这是绝对安全的。

不过,这可能不是库代码的最佳选择。库代码通常缺乏上下文,并且不能很好地判断任何给定的错误条件是否是致命的。客户端代码可以更好地回答这个问题。

在继续探索替代方案之前,让我们以更紧凑的方式重写原始代码,将调用链接在一起,而不是将每个结果显式分配给变量:

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() return 与 document()location() 具有相同的值。除了链接调用之外,所提供的代码还进行了两项更改:

  • trim_start_matches() 传递的是字符文字,而不是字符串文字。这会生成最佳代码,而无需依赖编译器的优化器来确定长度为 1 的字符串正在尝试搜索单个字符。
  • return 值是通过调用 to_owned() 构造的。 format! 宏增加了开销,最终调用了 to_string()。虽然在这种情况下会表现出相同的行为,但使用语义上更准确的 to_owned() 函数可以帮助您在编译时捕获错误(例如,如果您不小心 returned 42.to_string())。

备选方案

实现此函数的一种更自然的方法是让它 return 要么是代表查询字符串的值,要么根本没有值。 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,
    }
}

这允许函数的客户端根据函数 return 是 Some(s) 还是 None 做出决定。这会将所有错误条件映射到 None 值。

如果希望将失败的原因返回给调用者,decode_request 函数可以选择 return 一个 Result 值,例如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())
}