Iterating over JavaScript Values
Methods That Return js_sys::Iterator
Some JavaScript collections have methods for iterating over their values or keys:
Map::values
Set::keys
- etc...
These methods return
js_sys::Iterator
,
which is the Rust representation of a JavaScript object that has a next
method
that either returns the next item in the iteration, notes that iteration has
completed, or throws an error. That is, js_sys::Iterator
represents an object
that implements the duck-typed JavaScript iteration
protocol.
js_sys::Iterator
can be converted into a Rust iterator either by reference
(into
js_sys::Iter<'a>
)
or by value (into
js_sys::IntoIter
). The
Rust iterator will yield items of type Result<JsValue>
. If it yields an
Ok(...)
, then the JS iterator protocol returned an element. If it yields an
Err(...)
, then the JS iterator protocol threw an exception.
#![allow(unused_variables)]
fn main() {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn count_strings_in_set(set: &js_sys::Set) -> u32 {
let mut count = 0;
// Call `keys` to get an iterator over the set's elements. Because this is
// in a `for ... in ...` loop, Rust will automatically call its
// `IntoIterator` trait implementation to convert it into a Rust iterator.
for x in set.keys() {
// We know the built-in iterator for set elements won't throw
// exceptions, so just unwrap the element. If this was an untrusted
// iterator, we might want to explicitly handle the case where it throws
// an exception instead of returning a `{ value, done }` object.
let x = x.unwrap();
// If `x` is a string, increment our count of strings in the set!
if x.is_string() {
count += 1;
}
}
count
}
}
Iterating Over Any JavaScript Object that Implements the Iterator Protocol
You could manually test for whether an object implements JS's duck-typed
iterator protocol, and if so, convert it into a js_sys::Iterator
that you can
finally iterate over. You don't need to do this by-hand, however, since we
bundled this up as the js_sys::try_iter
function!
For example, we can write a function that collects the numbers from any JS
iterable and returns them as an Array
:
#![allow(unused_variables)]
fn main() {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn collect_numbers(some_iterable: &JsValue) -> Result<js_sys::Array, JsValue> {
let nums = js_sys::Array::new();
let iterator = js_sys::try_iter(some_iterable)?.ok_or_else(|| {
"need to pass iterable JS values!"
})?;
for x in iterator {
// If the iterator's `next` method throws an error, propagate it
// up to the caller.
let x = x?;
// If `x` is a number, add it to our array of numbers!
if x.as_f64().is_some() {
nums.push(&x);
}
}
Ok(nums)
}
}