在 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())
}
上下文:我正在学习 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()
函数可以帮助您在编译时捕获错误(例如,如果您不小心 returned42.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())
}