Iterating over JavaScript Values
Methods That Return js_sys::Iterator
Some JavaScript collections have methods for iterating over their values or keys:
Map::valuesSet::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 and js_sys::AsyncIterator also support erasable generic type parameters like Iterator<T> and AsyncIterator<T>, allowing you to document the expected type of values yielded by the iterator. The generic type is erased at the ABI boundary, but provides type safety and documentation in your Rust code.
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)]
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)]
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)
}
}