# Modeling Rust trait objects subtyping relation via OCaml polymorphic variants

**URL:** <https://discuss.ocaml.org/t/modeling-rust-trait-objects-subtyping-relation-via-ocaml-polymorphic-variants/15644>\
**Category:** Learning\
**Tags:** polymorphic-variants, rust, subtyping, rust-traits\
**Created:** [November 21, 2024, 6:20am UTC](https://discuss.ocaml.org/t/modeling-rust-trait-objects-subtyping-relation-via-ocaml-polymorphic-variants/15644 "2024-11-21T06:20:22Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Konstantin\_Olkhovski](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/konstantin_olkhovski/32/1494_2.png) [@Konstantin\_Olkhovski](https://discuss.ocaml.org/u/Konstantin_Olkhovski)\
**Post date:** [November 21, 2024, 6:20am UTC](https://discuss.ocaml.org/t/modeling-rust-trait-objects-subtyping-relation-via-ocaml-polymorphic-variants/15644/1 "2024-11-21T06:20:22Z")

</div>

I’m trying to model Rust subtyping around trait objects in OCaml. I have made a universal smart pointer for ocaml-rs (repo [here](https://github.com/Lupus/ocaml-rs-smartptr) if someone is interested), which allows to store Rust objects or Rust trait objects within single OCaml Value, and pass it back to Rust stubs. The thing comes with dynamic type information registry which allows Rust side to coerce smart pointer to concrete type into smart pointer to trait object (only object-safe traits can be used for this). With some magic sprinkled on top, I can get a list of polymorphic variant constructors that represent which traits certain boxed object provides. What I’m trying to do is to improve developer experience and provide all methods that come from implemented traits right into final module, e.g. in example below I want `to_string` method from `Display` to be available directly within `Error` module, as `Error` trait includes `Display` trait from Rust side. It’s possible to call `Display.to_string` on `Error.t` with explicit coercion, but that is hard to discover when glancing at methids available in `Error` module in one’s IDE - one would have to go read the constructors in type definition.

```ocaml
module Display = struct
  type nonrec t = [`Std_fmt_display | `Core_marker_send] Ocaml_rs_smartptr.Rusty_obj.t

  external to_string : t -> string = "rstdlib_display_to_string"
end

module Error = struct
  type nonrec t =
    [ `Ocaml_stdlib_error_wrapper_error_wrapper
    | `Core_marker_send
    | `Std_error_error
    | `Std_fmt_display
    | `Core_fmt_debug
    ]
      Ocaml_rs_smartptr.Rusty_obj.t

  (* Attempt to include functions from Display as variant contractors are
     compatible fails :(
     
    In this `with' constraint, the new definition of t
    does not match its original definition in the constrained signature:
    Type declarations do not match:
      type t = t
    is not included in
      type t =
          [`Core_marker_send | `Std_fmt_display]
          Ocaml_rs_smartptr.Rusty_obj.intf
    The type
      t/2 =
        [ `Core_fmt_debug
        | `Core_marker_send
        | `Ocaml_stdlib_error_wrapper_error_wrapper
        | `Std_error_error
        | `Std_fmt_display ] Ocaml_rs_smartptr.Rusty_obj.intf
    is not equal to the type
      [`Core_marker_send | `Std_fmt_display] Ocaml_rs_smartptr.Rusty_obj.intf
    The second variant type does not allow tag(s)
    `Core_fmt_debug, `Ocaml_stdlib_error_wrapper_error_wrapper, `Std_error_errorocamllsp
  *)
  include (Display : module type of Display with type t := t)

  external source : t -> t option = "rstdlib_error_source"
end

let () =
  let error : Error.t = Obj.magic () in
  (* Explicit coercion below works *)
  let _ : string = Display.to_string (error :> Display.t) in
  ()
;;

```

`Rusty_obj.intf` is defined as follows in Rusty\_obj.mli:

```ocaml
type -'tags intf
type 'a t = ([>] as 'a) intf

```

Probably something is wrong with that covariant/contravariant modifiers for tags? Unfortunately I can’t wrap my head around this… Is there some way to achieve inclusion of `Display` module into `Error` module given that constructors are compatible? I could of course write a bunch of wrapper functions by hand, but that won’t scale. Set of constructors along with external method signatures are actually generated by `ocaml-gen` crate and I can’t modify it to produce something more complex from Rust side. Thus I wanted to include generated modules into my own, potentially add some convenience functions on top of generated ones, and add those include relations for convenience. Any help on this would be greatly appreciated.

---

<div class="post-metadata">

**Author:** ![octachron](https://avatars.discourse-cdn.com/v4/letter/o/49beb7/32.png) [@octachron](https://discuss.ocaml.org/u/octachron)\
**Post date:** [November 21, 2024, 8:57am UTC](https://discuss.ocaml.org/t/modeling-rust-trait-objects-subtyping-relation-via-ocaml-polymorphic-variants/15644/2 "2024-11-21T08:57:20Z")

</div>

> [@Konstantin\_Olkhovski](#):
>
> external to\_string : t → string = “rstdlib\_display\_to\_string”

A simple fix might be to ensure that `to_string` accept all object with at least the right traits:

```ocaml
module Display = struct
  type cap = [`Std_fmt_display | `Core_marker_send]
  type 'a t = ([> cap] as 'a) Ocaml_rs_smartptr.Rusty_obj.t
  external to_string : _ t -> string = "rstdlib_display_to_string"
end
module Error = struct
  include Display
  type cap =
    [ `Ocaml_stdlib_error_wrapper_error_wrapper
    | `Std_error_error
    | `Core_fmt_debug
    | Display.cap
    ]
   type 'a t = ([> cap] as 'a) Ocaml_rs_smartptr.Rusty_obj.t
  external source : 'a t -> 'a t option = "rstdlib_error_source"
end

```

---

<div class="post-metadata">

**Author:** ![Konstantin\_Olkhovski](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/konstantin_olkhovski/32/1494_2.png) [@Konstantin\_Olkhovski](https://discuss.ocaml.org/u/Konstantin_Olkhovski)\
**Post date:** [November 21, 2024, 9:42am UTC](https://discuss.ocaml.org/t/modeling-rust-trait-objects-subtyping-relation-via-ocaml-polymorphic-variants/15644/3 "2024-11-21T09:42:32Z")

</div>

Thanks @octachron , that seems to do the trick!

So now I have to tweak my stubs generator to produce Stubs.ml as follows:

```ocaml
module Display = struct
  type nonrec 'cap t =
    ([> `Std_fmt_display | `Core_marker_send] as 'cap) Ocaml_rs_smartptr.Rusty_obj.t

  external to_string : _ t -> string = "rstdlib_display_to_string"
end

module Error = struct
  module Wrapper = struct
    type nonrec 'cap t =
      ([> `Ocaml_stdlib_error_wrapper_error_wrapper
       | `Core_marker_send
       | `Std_error_error
       | `Std_fmt_display
       | `Core_fmt_debug
       ]
       as
       'cap)
        Ocaml_rs_smartptr.Rusty_obj.t
  end

  type nonrec 'cap t =
    ([> `Std_error_error | `Core_marker_send] as 'cap) Ocaml_rs_smartptr.Rusty_obj.t

  external source : _ t -> _ Wrapper.t option = "rstdlib_error_source"
end

```

which I can then wrap in higher level API like this:

```auto
module Display = struct
  include Stubs.Display
end

module Error = struct
  include Stubs.Error

  module Wrapper = struct
    include Display
    include Stubs.Error.Wrapper
  end
end

```

And use `Display` method right inside `Error.Wrapper` without any coerciens!

```auto
let () =
  let error : _ Error.Wrapper.t = Obj.magic () in
  let _ : string = Error.Wrapper.to_string error in
  ()
;;

```
