mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-14 21:41:23 +00:00
Implement Deref
for all imported JS types
This commit implements the first half of [RFC #5] where the `Deref` trait is implemented for all imported types. The target of `Deref` is either the first entry of the list of `extends` attribute or `JsValue`. All examples using `.as_ref()` with various `web-sys` types have been updated to the more ergonomic deref casts now. Additionally the `web-sys` generation of the `extends` array has been fixed slightly to explicitly list implementatoins in the hierarchy order to ensure the correct target for `Deref` is chosen. [RFC #5]: https://github.com/rustwasm/rfcs/blob/master/text/005-structural-and-deref.md
This commit is contained in:
@ -535,7 +535,7 @@ impl ToTokens for ast::ImportType {
|
|||||||
use wasm_bindgen::convert::RefFromWasmAbi;
|
use wasm_bindgen::convert::RefFromWasmAbi;
|
||||||
use wasm_bindgen::describe::WasmDescribe;
|
use wasm_bindgen::describe::WasmDescribe;
|
||||||
use wasm_bindgen::{JsValue, JsCast};
|
use wasm_bindgen::{JsValue, JsCast};
|
||||||
use wasm_bindgen::__rt::core::mem::ManuallyDrop;
|
use wasm_bindgen::__rt::core;
|
||||||
|
|
||||||
impl WasmDescribe for #rust_name {
|
impl WasmDescribe for #rust_name {
|
||||||
fn describe() {
|
fn describe() {
|
||||||
@ -589,13 +589,13 @@ impl ToTokens for ast::ImportType {
|
|||||||
|
|
||||||
impl RefFromWasmAbi for #rust_name {
|
impl RefFromWasmAbi for #rust_name {
|
||||||
type Abi = <JsValue as RefFromWasmAbi>::Abi;
|
type Abi = <JsValue as RefFromWasmAbi>::Abi;
|
||||||
type Anchor = ManuallyDrop<#rust_name>;
|
type Anchor = core::mem::ManuallyDrop<#rust_name>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
||||||
let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js, extra);
|
let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js, extra);
|
||||||
ManuallyDrop::new(#rust_name {
|
core::mem::ManuallyDrop::new(#rust_name {
|
||||||
obj: ManuallyDrop::into_inner(tmp),
|
obj: core::mem::ManuallyDrop::into_inner(tmp),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,6 +657,20 @@ impl ToTokens for ast::ImportType {
|
|||||||
};
|
};
|
||||||
}).to_tokens(tokens);
|
}).to_tokens(tokens);
|
||||||
|
|
||||||
|
let deref_target = match self.extends.first() {
|
||||||
|
Some(target) => quote! { #target },
|
||||||
|
None => quote! { JsValue },
|
||||||
|
};
|
||||||
|
(quote! {
|
||||||
|
impl core::ops::Deref for #rust_name {
|
||||||
|
type Target = #deref_target;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &#deref_target {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).to_tokens(tokens);
|
||||||
for superclass in self.extends.iter() {
|
for superclass in self.extends.iter() {
|
||||||
(quote! {
|
(quote! {
|
||||||
impl From<#rust_name> for #superclass {
|
impl From<#rust_name> for #superclass {
|
||||||
|
@ -7,7 +7,8 @@ extern crate wasm_bindgen_futures;
|
|||||||
extern crate wasm_bindgen_test;
|
extern crate wasm_bindgen_test;
|
||||||
extern crate web_sys;
|
extern crate web_sys;
|
||||||
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
use wasm_bindgen::{JsValue, JsCast};
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
wasm_bindgen_test_configure!(run_in_browser);
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
@ -56,3 +57,11 @@ pub mod table_element;
|
|||||||
pub mod title_element;
|
pub mod title_element;
|
||||||
pub mod xpath_result;
|
pub mod xpath_result;
|
||||||
pub mod indexeddb;
|
pub mod indexeddb;
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn deref_works() {
|
||||||
|
let x = JsValue::from(3);
|
||||||
|
let x = x.unchecked_into::<web_sys::XmlHttpRequestUpload>();
|
||||||
|
let y: &web_sys::XmlHttpRequestEventTarget = &x;
|
||||||
|
drop(y);
|
||||||
|
}
|
||||||
|
@ -735,11 +735,17 @@ impl<'src> FirstPass<'src, ()> for weedle::CallbackInterfaceDefinition<'src> {
|
|||||||
impl<'a> FirstPassRecord<'a> {
|
impl<'a> FirstPassRecord<'a> {
|
||||||
pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator<Item = String> + 'me {
|
pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator<Item = String> + 'me {
|
||||||
let mut set = BTreeSet::new();
|
let mut set = BTreeSet::new();
|
||||||
self.fill_superclasses(interface, &mut set);
|
let mut list = Vec::new();
|
||||||
set.into_iter()
|
self.fill_superclasses(interface, &mut set, &mut list);
|
||||||
|
list.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_superclasses(&self, interface: &str, set: &mut BTreeSet<String>) {
|
fn fill_superclasses(
|
||||||
|
&self,
|
||||||
|
interface: &str,
|
||||||
|
set: &mut BTreeSet<&'a str>,
|
||||||
|
list: &mut Vec<String>,
|
||||||
|
) {
|
||||||
let data = match self.interfaces.get(interface) {
|
let data = match self.interfaces.get(interface) {
|
||||||
Some(data) => data,
|
Some(data) => data,
|
||||||
None => return,
|
None => return,
|
||||||
@ -749,8 +755,9 @@ impl<'a> FirstPassRecord<'a> {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
if self.interfaces.contains_key(superclass) {
|
if self.interfaces.contains_key(superclass) {
|
||||||
if set.insert(camel_case_ident(superclass)) {
|
if set.insert(superclass) {
|
||||||
self.fill_superclasses(superclass, set);
|
list.push(camel_case_ident(superclass));
|
||||||
|
self.fill_superclasses(superclass, set, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,7 @@ pub fn run() -> Result<(), JsValue> {
|
|||||||
let val = document.create_element("p")?;
|
let val = document.create_element("p")?;
|
||||||
val.set_inner_html("Hello from Rust!");
|
val.set_inner_html("Hello from Rust!");
|
||||||
|
|
||||||
// Right now the class inheritance hierarchy of the DOM isn't super
|
body.append_child(&val)?;
|
||||||
// ergonomic, so we manually cast `val: Element` to `&Node` to call the
|
|
||||||
// `append_child` method.
|
|
||||||
AsRef::<web_sys::Node>::as_ref(&body).append_child(val.as_ref())?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -8,29 +8,19 @@ use wasm_bindgen::prelude::*;
|
|||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn main() {
|
pub fn main() -> Result<(), JsValue> {
|
||||||
let document = web_sys::window().unwrap().document().unwrap();
|
let document = web_sys::window().unwrap().document().unwrap();
|
||||||
let canvas = document
|
let canvas = document
|
||||||
.create_element("canvas")
|
.create_element("canvas")?
|
||||||
.unwrap()
|
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
document.body().unwrap().append_child(&canvas)?;
|
||||||
.map_err(|_| ())
|
|
||||||
.unwrap();
|
|
||||||
(document.body().unwrap().as_ref() as &web_sys::Node)
|
|
||||||
.append_child(canvas.as_ref() as &web_sys::Node)
|
|
||||||
.unwrap();
|
|
||||||
canvas.set_width(640);
|
canvas.set_width(640);
|
||||||
canvas.set_height(480);
|
canvas.set_height(480);
|
||||||
(canvas.as_ref() as &web_sys::HtmlElement)
|
canvas.style().set_property("border", "solid")?;
|
||||||
.style()
|
|
||||||
.set_property("border", "solid")
|
|
||||||
.unwrap();
|
|
||||||
let context = canvas
|
let context = canvas
|
||||||
.get_context("2d")
|
.get_context("2d")?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.dyn_into::<web_sys::CanvasRenderingContext2d>()?;
|
||||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
|
||||||
.unwrap();
|
|
||||||
let context = Rc::new(context);
|
let context = Rc::new(context);
|
||||||
let pressed = Rc::new(Cell::new(false));
|
let pressed = Rc::new(Cell::new(false));
|
||||||
{
|
{
|
||||||
@ -41,9 +31,10 @@ pub fn main() {
|
|||||||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||||
pressed.set(true);
|
pressed.set(true);
|
||||||
}) as Box<FnMut(_)>);
|
}) as Box<FnMut(_)>);
|
||||||
(canvas.as_ref() as &web_sys::EventTarget)
|
canvas.add_event_listener_with_callback(
|
||||||
.add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())
|
"mousedown",
|
||||||
.unwrap();
|
closure.as_ref().unchecked_ref(),
|
||||||
|
)?;
|
||||||
closure.forget();
|
closure.forget();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -57,9 +48,10 @@ pub fn main() {
|
|||||||
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
context.move_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||||
}
|
}
|
||||||
}) as Box<FnMut(_)>);
|
}) as Box<FnMut(_)>);
|
||||||
(canvas.as_ref() as &web_sys::EventTarget)
|
canvas.add_event_listener_with_callback(
|
||||||
.add_event_listener_with_callback("mousemove", closure.as_ref().unchecked_ref())
|
"mousemove",
|
||||||
.unwrap();
|
closure.as_ref().unchecked_ref(),
|
||||||
|
)?;
|
||||||
closure.forget();
|
closure.forget();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -70,9 +62,12 @@ pub fn main() {
|
|||||||
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
|
context.line_to(event.offset_x() as f64, event.offset_y() as f64);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
}) as Box<FnMut(_)>);
|
}) as Box<FnMut(_)>);
|
||||||
(canvas.as_ref() as &web_sys::EventTarget)
|
canvas.add_event_listener_with_callback(
|
||||||
.add_event_listener_with_callback("mouseup", closure.as_ref().unchecked_ref())
|
"mouseup",
|
||||||
.unwrap();
|
closure.as_ref().unchecked_ref(),
|
||||||
|
)?;
|
||||||
closure.forget();
|
closure.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ impl WorkerPool {
|
|||||||
// thread?
|
// thread?
|
||||||
// * Need to handle the `?` on `post_message` as well.
|
// * Need to handle the `?` on `post_message` as well.
|
||||||
array.push(&wasm_bindgen::memory());
|
array.push(&wasm_bindgen::memory());
|
||||||
worker.post_message(array.as_ref())?;
|
worker.post_message(&array)?;
|
||||||
worker.set_onmessage(Some(callback.as_ref().unchecked_ref()));
|
worker.set_onmessage(Some(callback.as_ref().unchecked_ref()));
|
||||||
worker.set_onerror(Some(callback.as_ref().unchecked_ref()));
|
worker.set_onerror(Some(callback.as_ref().unchecked_ref()));
|
||||||
workers.push(worker);
|
workers.push(worker);
|
||||||
@ -355,10 +355,10 @@ impl Shared {
|
|||||||
self.scene.height as f64,
|
self.scene.height as f64,
|
||||||
)?;
|
)?;
|
||||||
let arr = Array::new();
|
let arr = Array::new();
|
||||||
arr.push(data.as_ref());
|
arr.push(&data);
|
||||||
arr.push(&JsValue::from(done));
|
arr.push(&JsValue::from(done));
|
||||||
arr.push(&JsValue::from(self.id as f64));
|
arr.push(&JsValue::from(self.id as f64));
|
||||||
global.post_message(arr.as_ref())?;
|
global.post_message(&arr)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ extern crate wasm_bindgen;
|
|||||||
extern crate web_sys;
|
extern crate web_sys;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::{AudioContext, AudioNode, OscillatorType};
|
use web_sys::{AudioContext, OscillatorType};
|
||||||
|
|
||||||
/// Converts a midi note to frequency
|
/// Converts a midi note to frequency
|
||||||
///
|
///
|
||||||
@ -60,34 +60,24 @@ impl FmOsc {
|
|||||||
fm_osc.set_type(OscillatorType::Sine);
|
fm_osc.set_type(OscillatorType::Sine);
|
||||||
fm_osc.frequency().set_value(0.0);
|
fm_osc.frequency().set_value(0.0);
|
||||||
|
|
||||||
// Create base class references:
|
|
||||||
{
|
|
||||||
let primary_node: &AudioNode = primary.as_ref();
|
|
||||||
let gain_node: &AudioNode = gain.as_ref();
|
|
||||||
let fm_osc_node: &AudioNode = fm_osc.as_ref();
|
|
||||||
let fm_gain_node: &AudioNode = fm_gain.as_ref();
|
|
||||||
let destination = ctx.destination();
|
|
||||||
let destination_node: &AudioNode = destination.as_ref();
|
|
||||||
|
|
||||||
// Connect the nodes up!
|
// Connect the nodes up!
|
||||||
|
|
||||||
// The primary oscillator is routed through the gain node, so that
|
// The primary oscillator is routed through the gain node, so that
|
||||||
// it can control the overall output volume.
|
// it can control the overall output volume.
|
||||||
primary_node.connect_with_audio_node(gain.as_ref())?;
|
primary.connect_with_audio_node(&gain)?;
|
||||||
|
|
||||||
// Then connect the gain node to the AudioContext destination (aka
|
// Then connect the gain node to the AudioContext destination (aka
|
||||||
// your speakers).
|
// your speakers).
|
||||||
gain_node.connect_with_audio_node(destination_node)?;
|
gain.connect_with_audio_node(&ctx.destination())?;
|
||||||
|
|
||||||
// The FM oscillator is connected to its own gain node, so it can
|
// The FM oscillator is connected to its own gain node, so it can
|
||||||
// control the amount of modulation.
|
// control the amount of modulation.
|
||||||
fm_osc_node.connect_with_audio_node(fm_gain.as_ref())?;
|
fm_osc.connect_with_audio_node(&fm_gain)?;
|
||||||
|
|
||||||
|
|
||||||
// Connect the FM oscillator to the frequency parameter of the main
|
// Connect the FM oscillator to the frequency parameter of the main
|
||||||
// oscillator, so that the FM node can modulate its frequency.
|
// oscillator, so that the FM node can modulate its frequency.
|
||||||
fm_gain_node.connect_with_audio_param(&primary.frequency())?;
|
fm_gain.connect_with_audio_param(&primary.frequency())?;
|
||||||
}
|
|
||||||
|
|
||||||
// Start the oscillators!
|
// Start the oscillators!
|
||||||
primary.start()?;
|
primary.start()?;
|
||||||
|
@ -8,20 +8,16 @@ use web_sys::{WebGlProgram, WebGlRenderingContext, WebGlShader};
|
|||||||
use js_sys::{WebAssembly};
|
use js_sys::{WebAssembly};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn draw() {
|
pub fn draw() -> Result<(), JsValue> {
|
||||||
let document = web_sys::window().unwrap().document().unwrap();
|
let document = web_sys::window().unwrap().document().unwrap();
|
||||||
let canvas = document.get_element_by_id("canvas").unwrap();
|
let canvas = document.get_element_by_id("canvas").unwrap();
|
||||||
let canvas: web_sys::HtmlCanvasElement = canvas
|
let canvas: web_sys::HtmlCanvasElement = canvas
|
||||||
.dyn_into::<web_sys::HtmlCanvasElement>()
|
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||||
.map_err(|_| ())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let context = canvas
|
let context = canvas
|
||||||
.get_context("webgl")
|
.get_context("webgl")?
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.dyn_into::<WebGlRenderingContext>()?;
|
||||||
.dyn_into::<WebGlRenderingContext>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let vert_shader = compile_shader(
|
let vert_shader = compile_shader(
|
||||||
&context,
|
&context,
|
||||||
@ -32,7 +28,7 @@ pub fn draw() {
|
|||||||
gl_Position = position;
|
gl_Position = position;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
).unwrap();
|
)?;
|
||||||
let frag_shader = compile_shader(
|
let frag_shader = compile_shader(
|
||||||
&context,
|
&context,
|
||||||
WebGlRenderingContext::FRAGMENT_SHADER,
|
WebGlRenderingContext::FRAGMENT_SHADER,
|
||||||
@ -41,23 +37,23 @@ pub fn draw() {
|
|||||||
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
).unwrap();
|
)?;
|
||||||
let program = link_program(&context, [vert_shader, frag_shader].iter()).unwrap();
|
let program = link_program(&context, [vert_shader, frag_shader].iter())?;
|
||||||
context.use_program(Some(&program));
|
context.use_program(Some(&program));
|
||||||
|
|
||||||
let vertices: [f32; 9] = [-0.7, -0.7, 0.0, 0.7, -0.7, 0.0, 0.0, 0.7, 0.0];
|
let vertices: [f32; 9] = [-0.7, -0.7, 0.0, 0.7, -0.7, 0.0, 0.0, 0.7, 0.0];
|
||||||
let memory_buffer = wasm_bindgen::memory().dyn_into::<WebAssembly::Memory>().unwrap().buffer();
|
let memory_buffer = wasm_bindgen::memory().dyn_into::<WebAssembly::Memory>()?.buffer();
|
||||||
let vertices_location = vertices.as_ptr() as u32 / 4;
|
let vertices_location = vertices.as_ptr() as u32 / 4;
|
||||||
let vert_array = js_sys::Float32Array::new(&memory_buffer).subarray(
|
let vert_array = js_sys::Float32Array::new(&memory_buffer).subarray(
|
||||||
vertices_location,
|
vertices_location,
|
||||||
vertices_location + vertices.len() as u32,
|
vertices_location + vertices.len() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let buffer = context.create_buffer().unwrap();
|
let buffer = context.create_buffer().ok_or("failed to create buffer")?;
|
||||||
context.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
|
context.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&buffer));
|
||||||
context.buffer_data_with_array_buffer_view(
|
context.buffer_data_with_array_buffer_view(
|
||||||
WebGlRenderingContext::ARRAY_BUFFER,
|
WebGlRenderingContext::ARRAY_BUFFER,
|
||||||
vert_array.as_ref(),
|
&vert_array,
|
||||||
WebGlRenderingContext::STATIC_DRAW,
|
WebGlRenderingContext::STATIC_DRAW,
|
||||||
);
|
);
|
||||||
context.vertex_attrib_pointer_with_i32(0, 3, WebGlRenderingContext::FLOAT, false, 0, 0);
|
context.vertex_attrib_pointer_with_i32(0, 3, WebGlRenderingContext::FLOAT, false, 0, 0);
|
||||||
@ -71,6 +67,7 @@ pub fn draw() {
|
|||||||
0,
|
0,
|
||||||
(vertices.len() / 3) as i32,
|
(vertices.len() / 3) as i32,
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_shader(
|
pub fn compile_shader(
|
||||||
|
@ -83,6 +83,7 @@
|
|||||||
- [Cargo Features](./web-sys/cargo-features.md)
|
- [Cargo Features](./web-sys/cargo-features.md)
|
||||||
- [Function Overloads](./web-sys/function-overloads.md)
|
- [Function Overloads](./web-sys/function-overloads.md)
|
||||||
- [Type Translations](./web-sys/type-translations.md)
|
- [Type Translations](./web-sys/type-translations.md)
|
||||||
|
- [Inheritance](./web-sys/inheritance.md)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
55
guide/src/web-sys/inheritance.md
Normal file
55
guide/src/web-sys/inheritance.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Inheritance in `web-sys`
|
||||||
|
|
||||||
|
Inheritance between JS classes is the bread and butter of how the DOM works on
|
||||||
|
the web, and as a result it's quite important for `web-sys` to provide access to
|
||||||
|
this inheritance hierarchy as well! There are few ways you can access the
|
||||||
|
inheritance hierarchy when using `web-sys`.
|
||||||
|
|
||||||
|
### Accessing parent classes using `Deref`
|
||||||
|
|
||||||
|
Like smart pointers in Rust, all types in `web_sys` implement `Deref` to their
|
||||||
|
parent JS class. This means, for example, if you have a `web_sys::Element` you
|
||||||
|
can create a `web_sys::Node` from that implicitly:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let element: &Element = ...;
|
||||||
|
|
||||||
|
element.append_child(..); // call a method on `Node`
|
||||||
|
|
||||||
|
method_expecting_a_node(&element); // coerce to `&Node` implicitly
|
||||||
|
|
||||||
|
let node: &Node = &element; // explicitly coerce to `&Node`
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `Deref` allows ergonomic transitioning up the inheritance hierarchy to the
|
||||||
|
parent class and beyond, giving you access to all the methods using the `.`
|
||||||
|
operator.
|
||||||
|
|
||||||
|
### Accessing parent classes using `AsRef`
|
||||||
|
|
||||||
|
In addition to `Deref`, the `AsRef` trait is implemented for all types in
|
||||||
|
`web_sys` for all types in the inheritance hierarchy. For example for the
|
||||||
|
`HtmlAnchorElement` type you'll find:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl AsRef<HtmlElement> for HtmlAnchorElement
|
||||||
|
impl AsRef<Element> for HtmlAnchorElement
|
||||||
|
impl AsRef<Node> for HtmlAnchorElement
|
||||||
|
impl AsRef<EventTarget> for HtmlAnchorElement
|
||||||
|
impl AsRef<Object> for HtmlAnchorElement
|
||||||
|
impl AsRef<JsValue> for HtmlAnchorElement
|
||||||
|
```
|
||||||
|
|
||||||
|
You can use `.as_ref()` to explicitly get a reference to any parent class from
|
||||||
|
from a type in `web_sys`. Note that because of the number of `AsRef`
|
||||||
|
implementations you'll likely need to have type inference guidance as well.
|
||||||
|
|
||||||
|
### Accessing child clases using `JsCast`
|
||||||
|
|
||||||
|
Finally the `wasm_bindgen::JsCast` trait can be used to implement all manner of
|
||||||
|
casts between types. It supports static unchecked casts between types as well as
|
||||||
|
dynamic runtime-checked casts (using `instanceof`) between types.
|
||||||
|
|
||||||
|
More documentation about this can be found [on the trait itself][jscast]
|
||||||
|
|
||||||
|
[jscast]: https://docs.rs/wasm-bindgen/0.2/wasm_bindgen/trait.JsCast.html
|
Reference in New Issue
Block a user