2

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

我编写了这个函数来返回?删除前导的查询字符串:

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

这当然是一种改进。

4

1 回答 1

3

这个问题实际上是关于 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板条箱不熟悉,所以有一些猜测。即,假设返回与'swindow.location()相同的值。除了链接调用之外,呈现的代码还采用了另外两个更改:document()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值,例如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())
}
于 2020-05-03T18:41:37.930 回答