Integrating Julia with Rust?

Interested in the results / experiences who may have taken this approach - using Julia’s strengths in scientific computation with Rust’s efficiency and superior packaging via Cargo.

Integrating Julia WITHIN Rust implies embedding Julia inside a Rust project or making Julia a component that runs within a Rust program.

Integrating Julia with Rust refers to using both languages in collaboration, potentially calling one from the other, but not necessarily embedding.

3 Likes

I think you’ll get some pushback on the idea that using Cargo would be a clear improvement over Julia’s amazing built-in packaging system.

For example, Cargo relies on build scripts to support non-Rust dependencies. Julia used to do this, but deployment of non-Julia dependencies was a huge pain because it relied on users having working non-Julia build systems. Nowadays in Julia we have JLL packages and Yggdrasil.

There have been some efforts on calling Julia from Rust using Rust’s FFI, since in that sense Julia can be called like any other C library: I wrote Rust bindings for Julia. If you're interested in calling Julia from Rust, please have a look and tell me what you think

And for the reverse, here’s an example of calling Rust from Julia: Simple example of calling Rust from Julia

See also: [ANN] Embedding Rust Library in a Julia Package

I’m not sure how widely used these are; so far, it seems Julia and Rust have mostly been applied to very different kinds of software and applications.

The advantage of Rust vs. Julia is not really runtime efficiency (putting aside compiler latency), but the age-old tradeoffs of static typing (stronger compile-time guarantees, compact AOT binaries) vs. dynamic typing (e.g. easier exploration/prototyping, interactive JIT environments).

But there have been many “Julia vs. Rust” threads on this forum (rehashing the many decades of debates on static vs. dynamic languages), e.g.

So probably it isn’t productive to start yet another general thread on this topic — let’s keep this focused on interop.

10 Likes

It is possible to call Rust (dynamic) libraries from Julia, with ccall keyword. It implies C-like API of your Rust code used (or Fortran, or C++, or well C) used.

I.e. you need to add extern "C" to functions you call (and possibly also use #[repr(C)], i.e. for Rust structs, but I think you don’t need to do that always, even never, i.e. if they are not involved in interop).

https://docs.rust-embedded.org/book/interoperability/c-with-rust.html

I believe e.g. this is an example of that Polars.jl.

It IS also possible to call Rust from Julia (at least up to 1.10 LTS and 1.11), i.e. embed Julia in Rust, with a package for that, like you can embed Julia into Python, R etc.

I believe in the developer since the package (that I haven’t used) seems very well documented and well received, and has supported across many Julia versions with it’s “unstable C API”.

E.g. the internal C [non-]API of Julia was broken with 1.11, why juliacall for R is in a bit of trouble with a trivial fix though.

The package for Rust is not in trouble, I see it was fixed for 1.11, and is tested for it:

Array functions that have been removed in Julia 1.11, like jl_reshape_array, have been removed completely. Call the Julia function instead.

I wouldn’t worry about being able to call Julia from Rust, since you can stay with 1.10 LTS version, that was very recently declared, and thus will be supported for (I assume) the next 3+ years. If 1.12+ breaks something I’m confident it will be fixed in interop packages, but if you’re really worried about maintainers going missing then you have at least 1.10 and 1.11 to call, and Rust is a very important language that I’m sure someone would take over.

I note it’s not yet possible to call Python 3.13 from Julia (nor call from it likely), I filed an issue, it seems Python might have broken its API, or if not PythonCall.jl relied on internals. Python does have a stable API (also another level limited API, then internals, unstable, and they are changing unusually many now).

See also:

1 Like

+1 for Cargo - it’s years ahead of anything else, Python’s pip included…

Not sure what you mean by efficiency though. I recently ran a short project to benchmark Julia against Rust and a few other languages in the context of numerical calculation codes with very tight/hot loops, small cache usage, few/zero mallocs/frees.

Julia won, by a small margin. My testing was not especially extensive. I don’t doubt that Rust is the better choice if you need predictable latency. That seems obvious since it doesn’t have GC.

1 Like

I’m not sure what the point of calling Julia from Rust would be.

You call C code from Python because you want to speed up a calculation by writing a static typed (and thus fast, but not flexible) implementation.

Similarly, it would make sense to call code written in C++ or Rust from Python.

The use case for calling compiled language code from Julia is less strong. Julia is already fast, and it is compiled to machine code by the JIT rather than being a bytecode interpreter (Python).

IMO the advantage of Rust over Julia is the type system. You can essentially write code which is guaranteed to be correct using the type system. This makes unit testing even less important than it already is in the domain of statically typed compiled languages.

Put another way, static type systems give you some guarantees about the correctness of your code which dynamic languages cannot provide. This is why unit testing is so important in Python.

It is also important in other languages such as C++ and Rust, but here you only test the runtime values rather than both runtime values and runtime types.

In Rust, you can get very strong guarantees about your code by the fact that it successfully compiles. Rust support for unit testing is also first class, but it is also inherently less important due to the typesystem design.

1 Like

I hadn’t realized there were so many threads about Rust and Julia - I haven’t been back here for awhile. I’ve always had difficulty creating packages in Julia. Julia’s a powerful language, but I can never get through packaging without multiple failures and honestly, I’ve put in hours and used the recommended methods. I’m adept at navigating my Linux system, but something always fails.
So I’m not looking for troubleshooting help with Julia’s packaging method any longer - I’ve abandoned it.
I recently created a simple audio project using Julia to analyze and provide data from a sample audio wave and used Rust to package it. One try and Cargo lined it all up perfectly. It was very satisfying. Cargo works first time, every time - always. It’s for that reason that I involved Rust. I’m just a hobbyist and most responses here quickly put me out of my depth. But I know what works and what doesn’t - at least for me.

Nobody in this thread is telling you this, in fact the person you responded to explicitly recommended that the thread continue, with a focus on interop instead of the tangents he brought up. Pushback isn’t censorship, it’s just a clash of opinions, and as long as it’s respectful, nobody has to regret an opinion. Preferring Rust and cargo is certainly not strange; like Pkg, cargo heavily benefited from being designed in an age where trading packages over the internet are commonplace. I think if you take mild disagreements on the chin, you’ll find many things to appreciate in his comment, at the very least the effort he went to consolidate all those links to relevant threads.

8 Likes

Sorry I didn’t mean for my response to come across that way.

Allow me to say this, because I do agree with you regarding packaging.

Packaging in Julia is a lot like packaging in Python. It’s okay but it’s just not as good as other languages. You mentioned Rust. Rust is a bit exceptional, because the package manager Cargo is so good.

I think it also comes down to compiled vs dynamic too. Complied languages are very good at providing a standard binary blob which is convenient to deploy to production servers etc.

To be completely transparent with you, I haven’t yet figured out a really good way to deploy a Python application, except perhaps by using docker. I’m relatively new to Julia but so far the situation appears similar.

edward-quant: No, it wasn’t your response kind sir. Thanks for your input.

The main pain point we’ve (we = my coworkers and I) had with Julia/Rust interop (specifically calling Rust from Julia) is the fact that you can’t just dlclose a Rust library (at least on Mac*) and have to restart the Julia REPL every time you need to make a change to the Rust code. This coupled with JIT compilation means that you often have to spend minutes between iterations for every change you make. There are some workarounds, but nothing really that great. DaemonMode helps for the restarts, but it’s pretty clunky (or just doesn’t work?) to use if you have a REPL-based workflow (it’s really designed for calling scripts from the command line). PrecompileTools and PackageCompiler can be useful if you don’t need to change your Julia code often, but they end up being counterproductive when you do. Plus they don’t really work well for complex code bases (i.e. running the exact same code during a precompilation/package compiler workload doesn’t “stick” and you end up having to just recompile again when you run it in the real session).

*edited because I mistakenly said on Linux before

1 Like

Bit confused by this comment. The relevant comparison is Pkg.jl, surely? pip is considered to be pretty bad, as far as I am aware.

3 Likes

For speed… runtime or development speed. Julia’s code is generic by default seems (Rust can do it, but it also seems awkward like in C++). And even if not applying, then it makes perfect sense to call Julia, or at least use Julia [packages] somehow with Rust or any fast language. I was answering for e.g. (with Rust):

E.g. when you have superior state-of-the art code, like SciML, it and more packages have Python wrappers by now (e.g. PySR), and some Julia code has R wrappers, it’s not only helpful to call from (those) slower) languages, also from any language (Rust and others might never have similar software made, even if possible, a waste of time to reinvent), including as fast in theory or in practice i.e. Rust can also benefit. Rust has one feature enabling faster code than Julia (rearranging structs by default, it’s a hidden limitation in Julia and C/C++, there you use pragma, I suppose, just not sure how to do it in Julia, except manually rearranging). Rust sometimes is sometimes faster, can also be slower than Julia’s. The GC needs not be an issue, can actually be faster. Rust’s default strings have UTF-8 validation overhead, as an example of it slower, though you can use one of the many alternative string types there.

That said, it might be better to have Julia as your default language (e.g. because of excellent REPL, does Rust have any; available?), and call Rust, as I explained, but either is ok.

Python’s REPL is improved as of 3.13, but I’ve not looked into it. At least the default one before was inferior. R might have a nice one, RCall.jl has Julia’s plus R REPL mode.]

I understand your point. You want the dynamic nature of Julia, but in Rust.

Please excuse me for asking this question - but why not just use Julia?

(I have a feeling I know your answer but I want to hear what you have to say about it.)

On this point your problem will be twofold I suspect.

  • maintaining a code of multiple languages - one of the claimed advantages of Julia being you can write all your code in a single language, solving Python’s two language problem
  • I would imagine calling to and from different languages is inefficient. For example, to call Julia you presumably have to spin up a copy of the compiler process, then start it off running some Julia code. Considering there’s a bit of latency there, unless there’s a way to write your calling code such that this only happens once, I would imaging the performance of any such application would be poor. Generally speaking, calling to and from different languages creates quite poor performance

One of the things I find myself missing from Rust, when using Julia is the distinction between String, OsString and the path version (forget the name)

I don’t believe it does - and it probably never will. Just as Cargo was good from the start because it was a core design consideration, I suspect there will be no REPL because it isn’t something which is on Rust’s radar - being a language primarily targeted at systems development. (That’s why doing math with it is a bit inconvenient.)

Don’t get too excited - Python is decades behind Julia in terms of JIT and performance generally. IMHO trying to JIT Python to make it fast is a bit pointless when we have Julia. Better to spend the developer hours building Python’s ecosystem for Julia

1 Like

Possibly a dumb suggestion but can you write unit tests on the Rust side for your Rust implementations, thus cutting out the need to load code with Julia?

Is pip bad? I’m coming at this from the point of view of C++ Makefiles being the benchmark for what is considered “bad”. Or cmake. Arguably worse.

I can’t see that pkg is better than pip. Is it? If so why? Personally I prefer pip and venv. But that might be because I’m used to it.

Btw sorry for replying in random order, some of these posts didn’t show up until I refreshed.

I have limited experience with Rust, but as far as I know, Pkg.jl is basically cargo for Julia, with tight language integration, solid dependency resolution, integrated environment handling, guaranteed reproducibility, etc etc. Basically all modern features that pip lacks.

Is pip even considered the best package manager for Python? As far as I’ve understand, there’s lots of replacements for pip, because it’s considered out of date.

2 Likes

genuine question as someone with little experience with Rust or Cargo: what do you like about it that Julia can’t provide? are the differences between the two that cause you say Cargo > Pkg.jl more along the lines of “easily” fixable UX warts & feature-adds or is there something more fundamental?

Rust tends to do a lot of things right (IMO so does Julia; that is not a comparative statement and they can both be true at the same time :slight_smile: ) so if there’s something to learn from its package manager we should definitely try

2 Likes

I’d be interested in hearing what experiences you’ve had that have lead you to the impression that Cargo is a lot better than Pkg. I have quite a high opinion of both, but your comments suggest a different experience, and I’d be interested in hearing more.

6 Likes

The person quoted qualified the statements by saying they are pretty new to Julia. I think the statement that cargo is good and that pip and Pkg.jl (not named) are ok to bad was based on limited personal experience, not on researching and comparing the package managers.

3 Likes

not to turn this into a python-dogpile but I think if anybody has spent more than 5 minutes with pip and Pkg.jl each and still equates them that’s cause for concern lol

3 Likes