Replies: 4 comments
-
@dbrattli Fable to Rust bindings are unfortunately quite unintuitive and brittle at the moment. Normally if you have complicated glue code it's easier to have a Rust native module (lib.rs or main.rs) wrapper that then calls the Fable-generated (F# to Rust) library code. That said, it's definitely possible to do everything in F#, it's just that it's not that intuitive (yet) and you need to work around the issues to make it stick. Basically, the problem usually is mapping the F# generated Rust types to native Rust types. Here is one version that compiles:
module StringSum
open Fable.Core
open Fable.Core.Rust
// To avoid module do bindings, imports are wrapped in a func.
// We have to eventually find a more intuitive way for imports
// that doesn't use module do bindings, but this works for now.
// Native Rust module imports have to contain either '{' or '*'
let imports() =
import "pyo3::prelude::*" ""
import "pyo3::types::{PyCFunction}" ""
()
[<Erase; Emit("String")>]
type RustString = class end
[<Emit("String::from($0.as_str())")>]
let toRustString (s: string): RustString = nativeOnly
[<Erase; Emit("PyErr")>]
type PyErr = class end
type PyResult<'T> = Result<'T, PyErr>
[<Erase; Emit("Python")>]
type Python = class end
[<Erase; Emit("PyModule")>]
type PyModule = class end
[<Erase; Emit("PyCFunction")>]
type PyCFunction = class end
[<OuterAttr("pyfunction")>]
let sum_as_string (a: int) (b: int) : PyResult<RustString> =
Ok (toRustString(string (a + b)))
// workaround for emitting function names and reference names
let inline add_function m f =
emitExpr () (m + ".add_function(wrap_pyfunction!(" + f + ", " + m + ")?)?")
// workaround for emitting unit result wrapped in parenthesis
let inline ok() =
Ok (emitExpr () "()")
[<OuterAttr("pymodule")>]
let string_sum (_py: Python, [<ByRef>] m: PyModule) : PyResult<unit> =
add_function (nameof m) (nameof sum_as_string)
ok() |
Beta Was this translation helpful? Give feedback.
-
Thank you so much for this @ncave. I understand it can be hard to generate specific Rust binding code like this so really appreciate the help to get started. The plan is to spend some time during the holidays to see if it's easier to use Fable Rust as native bindings for Fable Python instead of investing more in a new language like Cython. This would enable Fable Rust to help the performance of Fable Python and we build and improve on what we already have instead of scaling out with yet another language (cython) which makes things much harder to maintain. I'll start with one of the simpler modules in Fable library and see if I can get all of this to work |
Beta Was this translation helpful? Give feedback.
-
@dbrattli Not a problem, please feel free to post any questions or suggestions you might have about |
Beta Was this translation helpful? Give feedback.
-
Hi @ncave, @alexswan10k I've decided to do some investigations using Rust for Python bindings, mostly since I want to learn Rust more than I want to learn Cython. Tried porting this example binding, and could use some suggestions and hints into how the Fable Rust works:
I tried porting it to Fable Rust like this:
... which generates the following Rust code which have a few problems:
What do you think? How could I improve this?
Is it possible to write the
string_sum
in F# or would you just have used Rust instead for the module generation and import the generatedsum_as_string
? I'm not sure how to handle the?
operator. Is there a way, or should I just match instead? But now I getthe trait bound
Result<(), PyErr>: Cloneis not satisfied
Another thing I could not figure out is how to generate Ok of unit i.e
Ok(())
.I'm sorry for my limited Fable Rust and Rust knowledge 😬 but would be awesome if we one day could e.g use Array.fs, List.fs compiled to Rust as extension modules for Python.
Beta Was this translation helpful? Give feedback.
All reactions