stdweb icon indicating copy to clipboard operation
stdweb copied to clipboard

Help with unsatisfied trait bound

Open NeverGivinUp opened this issue 6 years ago • 6 comments

I'v created the following stub geometry.rs

use webcore::value::Reference;
use webapi::svg_element::ISVGElement;
use webapi::event_target::{IEventTarget, EventTarget};
use webapi::node::{INode, Node};
use webapi::element::{IElement, Element};
use webapi::svg_element::SVGElement;
use super::super::types::animated_length::AnimatedLength;
use super::graphics::ISVGGraphicsElement;
use super::graphics::SVGGraphicsElement;

pub trait ISVGGeometryElement: ISVGGraphicsElement {

    /// The SVG standard allows user agents to guestimate path lengths
    /// [(Standard)](https://www.w3.org/TR/SVG11/paths.html#DistanceAlongAPath), because the
    /// precise calculation is computation heavy. SVG document authors may give a length computed
    /// in advance.
    //TODO shouldn't this return an option then? Shouldn't all "reflecting" properties return options?
    fn path_length(&self) -> f64 {
        js!( return @{self}.pathLength; ).try_into().unwrap()
    }


}

/// represents SVG elements whose rendering is defined by geometry with an equivalent path,
/// and which can be filled and stroked. This includes paths and the basic shapes
///
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement)
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "SVGGeometryElement")]
#[reference(subclass_of(EventTarget, Node, Element, SVGElement, SVGGraphicsElement))]
pub struct SVGGeometryElement(Reference);


impl IEventTarget for SVGGeometryElement {}

impl INode for SVGGeometryElement {}

impl IElement for SVGGeometryElement {}

impl ISVGElement for SVGGeometryElement {}

impl ISVGGraphicsElement for SVGGeometryElement {}

impl ISVGGeometryElement for SVGGeometryElement {}

for which I get the following error

error[E0277]: the trait bound `webcore::newtype::Newtype<_, &Self>: webcore::serialization::JsSerializeOwned` is not satisfied
  --> src\webcore\macros.rs:69:21
   |
69 |         let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name );
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `webcore::serialization::JsSerializeOwned` is not implemented for `webcore::newtype::Newtype<_, &Self>`
   | 
  ::: src\webapi\svg\elements\geometry.rs:19:9
   |
19 |         js!( return @{self}.pathLength; ).try_into().unwrap()
   |         --------------------------------- in this macro invocation
   |
   = help: the following implementations were found:
             <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), F> as webcore::serialization::JsSerializeOwned>
             <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), std::option::Option<F>> as webcore::serialization::JsSerializeOwned>
             <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), webcore::mutfn::Mut<F>> as webcore::serialization::JsSerializeOwned>
             <webcore::newtype::Newtype<(webcore::serialization::NonFunctionTag, ()), T> as webcore::serialization::JsSerializeOwned>
           and 81 others
note: required by `webcore::serialization::JsSerializeOwned::into_js_owned`
  --> src\webcore\serialization.rs:59:5
   |
59 |     fn into_js_owned< 'a >( value: &'a mut Option< Self > ) -> SerializedValue< 'a >;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Alas, the practical meaning of the error message eludes me. Which element needs to implement JsSerializeOwned and how can I make it implement it (as it seems, this is happening somewhere in the macro-magicks)?

NeverGivinUp avatar Dec 27 '18 14:12 NeverGivinUp

You need to use self.as_ref()

What the error message means is basically "this type cannot be sent to JS".

With your ISVGGeometryElement trait, you are trying to send Self to JS, but there's no guarantee that Self can be sent to JS (any Rust type might implement ISVGGeometryElement).

By using .as_ref() you are converting the type into &Reference, which can be sent to JS.

Here is where the AsRef impl is defined.

P.S. As a general rule we try to avoid all-caps names like "SVG", so it should be SvgGeometryElement, SvgGraphicsElement, etc.

Pauan avatar Dec 27 '18 19:12 Pauan

You need to use self.as_ref()

This works. Thanks. So as a rule, I understand that I need to use .as_ref() in the js! macro whenever self is not a leaf of the inheritance tree.

The now changed method

    fn path_length(&self) -> f64 {
        js!(
            return @{self.as_ref()}.pathLength;
        ).try_into().unwrap()
    }

produces the following compile error (which had been there before, but I hoped it was just a consequence of the error I posted originally)

warning: a method with this name may be added to the standard library in the future
  --> src\webapi\svg\elements\geometry.rs:20:11
   |
20 |         ).try_into().unwrap()
   |           ^^^^^^^^
   |
   = note: #[warn(unstable_name_collisions)] on by default
   = warning: once this method is added to the standard library, the ambiguity may cause an error or change in behavior!
   = note: for more information, see issue #48919 <https://github.com/rust-lang/rust/issues/48919>
   = help: call with fully qualified syntax `webcore::try_from::TryInto::try_into(...)` to keep using the current method
   = help: add #![feature(try_from)] to the crate attributes to enable `std::convert::TryInto::try_into`

error[E0599]: no method named `try_into` found for type `webcore::value::Value` in the current scope
   --> src\webapi\svg\elements\geometry.rs:20:11
    |
20  |         ).try_into().unwrap()
    |           ^^^^^^^^
    | 
   ::: src\webcore\value.rs:161:1
    |
161 | pub enum Value {
    | -------------- method `try_into` not found for this
    |
    = help: items from traits can only be used if the trait is in scope
    = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
            `use webcore::try_from::TryInto;`

Can you tell me about the rules of coping with the return types of the js! macro? For return types, which are on the other side of the boundary, I need to use .into_reference_unchecked().unwrap() instead of .try_into().unwrap(). Are there any other cases I should be aware of?

NeverGivinUp avatar Dec 27 '18 21:12 NeverGivinUp

So as a rule, I understand that I need to use .as_ref() in the js! macro whenever self is not a leaf of the inheritance tree.

You need to use it whenever you are creating traits (such as ISVGGeometryElement).

produces the following compile error (which had been there before, but I hoped it was just a consequence of the error I posted originally)

Since TryInto is a trait, you must use it before you can use its methods (this is how all traits work):

use webcore::try_from::TryInto;

The bottom of the error message contains this helpful text:

    = help: items from traits can only be used if the trait is in scope
    = note: the following trait is implemented but not in scope, perhaps add a `use` for it:
            `use webcore::try_from::TryInto;`

Often times the Rust error messages contain helpful text like that, so it's good to read them carefully.

In this particular case the warning: a method with this name may be added to the standard library in the future is wrong, and actually causes a lot of confusion, so you should ignore it.

As a general rule (not just with stdweb, but with Rust in general) you should try to fix errors first, and only afterwards fix warrnings.

Can you tell me about the rules of coping with the return types of the js! macro? For return types, which are on the other side of the boundary, I need to use .into_reference_unchecked().unwrap() instead of .try_into().unwrap(). Are there any other cases I should be aware of?

There's very few cases where you need to use into_reference_unchecked. You should always try to use .try_into() if you can.

Pauan avatar Dec 27 '18 22:12 Pauan

Thank you @Pauan! Both of your suggestions worked very well. (And also I did underestimate the usefulness of the fineprint of the error messages!)

I have come across a case now, where I get the error about an unsatisfied trait bound for JsSerializeOwned, though I did not define a trait -- SVGPreserveAspectRatio is an interface to an attribute --, and using .as_ref() makes no difference. I have similar files -- for data types, not for attributes -- that do not produce errors. Any suggestions for this?

This is the file psa.rs

use webcore::value::Reference;
use webcore::try_from::TryInto;
use ::webapi::dom_exception::{NoModificationAllowedError, SyntaxError, NotSupportedError};

/// Indicates whether to force uniform scaling and, if so, the alignment method to use in case the
/// aspect ratio of the viewBox doesn't match the aspect ratio of the viewport.
/// [(Docs)](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio#Syntax)
#[derive(Copy, Clone, PartialEq, Eq, Debug, CLikeFrom)]
pub enum Alignment {
    ///The enumeration was set to a value that is not one of predefined types. It is invalid to
    /// attempt to define a new value of this type or to attempt to switch an existing value to
    /// this type.
    Unknown = 0,
    ///Do not force uniform scaling.
    ///
    /// Scale the graphic content of the given element non-uniformly
    /// if necessary such that the element's bounding box exactly matches the viewport rectangle.
    ///
    /// Note that if `<align>` is `none`, then the optional `<meetOrSlice>` value is ignored.
    None = 1,

    ///Force uniform scaling.
   ///
   ///Align the <min-x> of the element's viewBox with the smallest X value of the viewport.
   ///
   ///Align the <min-y> of the element's viewBox with the smallest Y value of the viewport.
    XMinYMin = 2,
    ///Force uniform scaling.
    ///
    ///Align the midpoint X value of the element's viewBox with the midpoint X value of the
    /// viewport.
    ///
    ///Align the <min-y> of the element's viewBox with the smallest Y value of the viewport.
    XMidYMin = 3,
    ///Force uniform scaling.
   ///
   ///Align the <min-x> + < width> of the element's viewBox with the maximum X value of the
   /// viewport.
   ///
   ///Align the <min-y> of the element's viewBox with the smallest Y value of the viewport.
    XMaxYMin = 4,

    ///Force uniform scaling.
    ///
    ///Align the <min-x> of the element's viewBox with the smallest X value of the viewport.
    ///
    ///Align the midpoint Y value of the element's viewBox with the midpoint Y value of the
    /// viewport.
    XMinYMid = 5,
    /// (the default ) - Force uniform scaling.
    ///
    ///Align the midpoint X value of the element's viewBox with the midpoint X value of the
    /// viewport.
    ///
    ///Align the midpoint Y value of the element's viewBox with the midpoint Y value of the
    /// viewport.
    XMidYMid = 6,
    ///Force uniform scaling.
    ///
    ///Align the <min-x> + < width> of the element's viewBox with the maximum X value of the
    /// viewport.
    ///
    ///Align the midpoint Y value of the element's viewBox with the midpoint Y value of the
    /// viewport.
    XMaxYMid = 7,

    ///Force uniform scaling.
   ///
   ///Align the <min-x> of the element's viewBox with the smallest X value of the viewport.
   ///
   ///Align the <min-y> + < height> of the element's viewBox with the maximum Y value of the
   /// viewport.
    XMinYMax = 8,
    //Force uniform scaling.
    ///
    ///Align the midpoint X value of the element's viewBox with the midpoint X value of the
    /// viewport.
    ///
    ///Align the <min-y> + < height> of the element's viewBox with the maximum Y value of the
    /// viewport.
    XMidYMax = 9,
    ///Force uniform scaling.
    ///
    ///Align the <min-x> + < width> of the element's viewBox with the maximum X value of the
    /// viewport.
    ///
    ///Align the <min-y> + < height> of the element's viewBox with the maximum Y value of the
    /// viewport.
    XMaxYMax = 10,
}


/// Optional parameter to PreserveAspectRatio, indicating whether to fill the view port as much as
/// possible, i.e. to `Meet` its bounds, or to cover it and to `Slice` (crop) the view_box, if
/// necessary.
/// [(Docs)](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio#Syntax)
#[derive(Copy, Clone, PartialEq, Eq, Debug, CLikeFrom)]
pub enum Mode {
    ///The enumeration was set to a value that is not one of predefined types. It is invalid to
    /// attempt to define a new value of this type or to attempt to switch an existing value to
    /// this type.
    Unknown = 0,
    ///(the default) - Scale the graphic such that:
    /// -   aspect ratio is preserved
    /// -   the entire viewBox is visible within the viewport
    /// -   the viewBox is scaled up as much as possible, while still meeting the other criteria
    ///
    ///In this case, if the aspect ratio of the graphic does not match the viewport, some of the
    /// viewport will extend beyond the bounds of the viewBox (i.e., the area into which the viewBox
    /// will draw will be smaller than the viewport).
    Meet = 1,
    ///Scale the graphic such that:
    ///
    ///-    aspect ratio is preserved
    ///-    the entire viewport is covered by the viewBox
    ///-    the viewBox is scaled down as much as possible, while still meeting the other criteria
    ///
    ///In this case, if the aspect ratio of the viewBox does not match the viewport, some of the
    /// viewBox will extend beyond the bounds of the viewport (i.e., the area into which the
    /// viewBox will draw is larger than the viewport).
    Slice = 2,
}

///Indicates how an element with a viewBox providing a given aspect ratio must fit into a viewport
/// with a different aspect ratio.
/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/SVGPreserveAspectRatio)
// https://www.w3.org/TR/SVG/coords.html#InterfaceSVGPreserveAspectRatio
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "SVGPreserveAspectRatio")]
pub struct PreserveAspectRatio(Reference);

impl PreserveAspectRatio {
    ///returns the alignment
    pub fn align(&self) -> Alignment {
        let num:usize = js!( return @{self}.align; ).try_into().unwrap();
        Alignment::from(num)
    }
    /// sets the alignement
    pub fn set_align(&self, alignment: Alignment) -> Result<(), NoModificationAllowedError> {
        let val = alignment as usize;
        js_try!(@(no_return)
                @{self.as_ref()}.align = @{val};).unwrap()
    }

    // returns the mode
    pub fn mode(&self) -> Mode {
        let num = js!( return @{self}.meetOrSlice; ).try_into().unwrap();
        Mode::from(num)
    }
    /// sets the mode
    pub fn set_mode(&self, mode: Mode) -> Result<(), NoModificationAllowedError> {
        let val = mode as usize;
        js_try!( @(no_return)
                 @{self}.meetOrSlice = @{val}; ).unwrap()
    }
}

Compilation produces these errors, which refer to set_align and set_mode

error[E0277]: the trait bound `webcore::newtype::Newtype<_, usize>: webcore::serialization::JsSerializeOwned` is not satisfied
   --> src\webcore\macros.rs:69:21
    |
69  |           let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name );
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `webcore::serialization::JsSerializeOwned` is not implemented for `webcore::newtype::Newtype<_, usize>`
    | 
   ::: src\webapi\svg\types\psa.rs:141:9
    |
141 | /         js_try!(@(no_return)
142 | |                 @{self.as_ref()}.align = @{val};).unwrap()
    | |_________________________________________________- in this macro invocation
    |
    = help: the following implementations were found:
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), F> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), std::option::Option<F>> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), webcore::mutfn::Mut<F>> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::NonFunctionTag, ()), T> as webcore::serialization::JsSerializeOwned>
            and 81 others
note: required by `webcore::serialization::JsSerializeOwned::into_js_owned`
   --> src\webcore\serialization.rs:59:5
    |
59  |     fn into_js_owned< 'a >( value: &'a mut Option< Self > ) -> SerializedValue< 'a >;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `webcore::newtype::Newtype<_, usize>: webcore::serialization::JsSerializeOwned` is not satisfied
   --> src\webcore\macros.rs:69:21
    |
69  |           let $name = $crate::private::JsSerializeOwned::into_js_owned( &mut $name );
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `webcore::serialization::JsSerializeOwned` is not implemented for `webcore::newtype::Newtype<_, usize>`
    | 
   ::: src\webapi\svg\types\psa.rs:153:9
    |
153 | /         js_try!( @(no_return)
154 | |                  @{self}.meetOrSlice = @{val}; ).unwrap()
    | |________________________________________________- in this macro invocation
    |
    = help: the following implementations were found:
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), F> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), std::option::Option<F>> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::FunctionTag, (A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)), webcore::mutfn::Mut<F>> as webcore::serialization::JsSerializeOwned>
              <webcore::newtype::Newtype<(webcore::serialization::NonFunctionTag, ()), T> as webcore::serialization::JsSerializeOwned>
            and 81 others
note: required by `webcore::serialization::JsSerializeOwned::into_js_owned`
   --> src\webcore\serialization.rs:59:5
    |
59  |     fn into_js_owned< 'a >( value: &'a mut Option< Self > ) -> SerializedValue< 'a >;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

NeverGivinUp avatar Dec 29 '18 00:12 NeverGivinUp

I did underestimate the usefulness of the fineprint of the error messages

I did too, when I was learning Rust.

I have come across a case now, where I get the error about an unsatisfied trait bound for JsSerializeOwned, though I did not define a trait

I don't think stdweb supports sending usize to JS, so it should be u32 instead.

Pauan avatar Dec 29 '18 21:12 Pauan

P.S. Here is all of the types which stdweb can send to JS:

https://docs.rs/stdweb/0.4.12/stdweb/trait.JsSerialize.html

Pauan avatar Dec 29 '18 21:12 Pauan