【问题标题】:Getting query string from Window object in WebAssembly in Rust从 Rust 的 WebAssembly 中的 Window 对象获取查询字符串
【发布时间】:2020-08-17 02:55:59
【问题描述】:

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

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

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

这当然是一种改进。

【问题讨论】:

    标签: rust wasm-bindgen rust-wasm


    【解决方案1】:

    这个问题实际上是关于 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() 返回与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&lt;String, wasm_bindgen::JsValue&gt;。这样做时,实现可以利用? 运算符,以紧凑的方式将错误传播给调用者:

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

    【讨论】:

    • 谢谢,很好的反馈。添加了当我尝试纯链接方法时出现的错误。如果您想到这一点,那太好了,但无论哪种方式,您的回答都已经非常有帮助了。
    • @mat 我现在看到the errorsearch().unwrap() 返回一个String,它从未分配给变量;它的生命周期在语句的末尾结束,但 trim_start_matches 返回一个切片引用,该引用比临时的要长。我会更新答案来解决这个问题。
    • 在更新后的代码中,trim_start_matches 仍将切片引用返回到临时String。改变的是切片引用本身现在是临时的。它的生命终结与其所指的临时String 值相吻合。不再有机会创建悬空引用,借阅检查器又高兴了。
    • 谢谢。 Rust 处理来自大多数语言的数据的范围/生命周期的差异绝对是我仍在努力解决的部分。
    • @mat Lifetime 被限制在封闭范围内。这是一个相当传统的选择,与许多编程语言共享。事实上,如果this were C++,你会遇到同样的生命周期问题。 Rust 的独特之处在于它在引用和引用之间在编译时强制执行生命周期约束。 C++ 没有,无效代码编译得很好,甚至产生了所需的输出。驯服借用检查器当然是一种努力。如果你还没有,我推荐“Programming Rust”
    猜你喜欢
    • 2015-07-30
    • 1970-01-01
    • 1970-01-01
    • 2018-05-11
    • 2011-09-10
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多