# \[ANN\] jlrs 0.17: generic targets, foreign type support, Julia 1.9 support, and more!

**URL:** <https://discourse.julialang.org/t/ann-jlrs-0-17-generic-targets-foreign-type-support-julia-1-9-support-and-more/90823>\
**Category:** Package Announcements\
**Created:** [November 25, 2022, 6:06pm UTC](https://discourse.julialang.org/t/ann-jlrs-0-17-generic-targets-foreign-type-support-julia-1-9-support-and-more/90823 "2022-11-25T18:06:47Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Taaitaaiger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/taaitaaiger/32/49792_2.png) [@Taaitaaiger](https://discourse.julialang.org/u/Taaitaaiger)\
**Post date:** [November 25, 2022, 6:06pm UTC](https://discourse.julialang.org/t/ann-jlrs-0-17-generic-targets-foreign-type-support-julia-1-9-support-and-more/90823/1 "2022-11-25T18:06:47Z")

</div>

jlrs is a crate for the Rust programming language that provides access to most of the Julia C API, it can be used to embed Julia in Rust applications and to use functionality from the Julia C API when writing `ccall`able functions in Rust. Version 0.17 is compatible with Julia 1.8 by default, 1.6 if the `lts` feature is enabled, and Julia 1.9 if the `beta` feature is enabled. The minimum supported version of Rust is 1.65.

## Memory management

The biggest change in this version is a redesign of how jlrs ensures the Julia GC is aware of data that is currently used in Rust. This is now handled by a custom resizable stack, which has several advantages over the old system. For example, methods can no longer fail due to running out of stack space; methods like `Value::new` and `Call::call` that could only fail due to this limitation are now infallible and no longer return a `Result`. The async runtime can allocate a stack for each task which gets rid of the `Mode` trait. And, it’s now possible for a function called through `ccall` to throw an exception with `CCall::throw_exception`.

The `Scope` and `PartialScope` traits, which were previously used to ensure that newly allocated Julia data would remain valid by rooting that data, have been replaced with the `Target` trait. Unlike the old traits, when a target is used it depends on the type of the target whether or not the returned data is rooted. Thanks to this property, all methods that take a target can return either rooted or unrooted data. As a result, all methods that returned unrooted data, e.g. `Call::call_unrooted`, have been removed because this can now be achieved by calling `Call::call` with a non-rooting target. Methods that used to take an argument that implemented `Scope` now take an `ExtendedTarget` instead, which isn’t restricted to using an `Output` for the final result but can use any target.

The `Global` struct has been renamed to `Unrooted` because it’s a target that leaves the result unrooted. Any immutable reference to a target is a valid non-rooting target, you should rarely need to use an `Unrooted` compared to the more prominent role `Global` used to play. Methods and closures that previously took both a `Global` and a `GcFrame` now only take a `GcFrame` to account for this.

## Layout changes

Because all methods that return Julia data can return rooted or unrooted data depending on the used target, I’ve decided to get rid of a major difference between `Ref<T: Wrapper>` and `T: Wrapper`: both types now contain a non-null pointer internally. Methods that previously returned a `Ref` now take a target, if the data might be undefined the return type is wrapped in an `Option`.

The `ValidLayout` trait has been split into a `ValidLayout` and `ValidField` trait. `ValidLayout` can be implemented by all types that provide a valid layout for some type of Julia data, while `ValidField` can only be implemented by types that provide a valid layout when the type is used as a field type.

Due to these changes all bindings previously generated with JlrsReflect.jl are now invalid. A new version of that package has been released, generated bindings will derive `ValidField` in addition to `ValidLayout` whenever applicable.

## Improved borrow checking

`Array`s and `Value`s can be tracked, while they’re tracked it’s safe to access their contents. Tracking data no longer requires a `GcFrame`, rather it’s tracked using a global ledger inspired by neon. Thanks to this change, multiple arrays can be mutably borrowed at the same time and `ccall`ed functions that need to access array data no longer need to create a scope first.

## Foreign types

You can now create custom foreign types by implementing the `ForeignType` trait. Types that implement this trait can be moved from Rust to Julia, making Julia’s GC responsible for freeing this data. This can be useful if you want to embed a Rust crate into a Julia package, but is also used internally to implement the previously mentioned dynamic stack.

## Kind-of multithread async runtime

Blocking tasks can now be posted to Julia rather than being called from the runtime thread, which allows multiple blocking tasks to run in parallel. When a blocking task is posted a new thread is spawned in Julia which calls the blocking task through `ccall`.

A custom channel is now used to send tasks to the async runtime. The reason for this change is that the async runtime can make use of multiple threads when the `beta` feature is enabled.

## Julia 1.9 – actually multithreaded async runtime

The most exciting feature added to Julia 1.9 IMO is thread adoption, which allows multiple foreign threads to call into Julia. This lets jlrs create additional worker threads for the async runtime. The number of worker threads can be set with the runtime builder, when a task is sent to the runtime it can be handled by either the runtime thread or any of the worker threads.

Julia 1.9 can also make use of two thread pools, `:default` and `:interactive`. The number of threads allocated to this pool can be set with the runtime builder. The methods of the `CallAsync` trait all target one of these thread pools.

## No more import libraries

Import libraries don’t have to be generated anymore when using jlrs with the MSVC toolchain. Rust used to require these import libraries, but with the recently stabilized support for linking against a raw dylib this is no longer necessary.

## Example

```julia-auto
    use jlrs::prelude::*;

    fn main() {
    // Initializing Julia is unsafe because it can race with another crate
    // that does the same.
    let mut julia = unsafe { RuntimeBuilder::new().start().unwrap() };
    let mut frame = StackFrame::new();
    let mut julia = julia.instance(&mut frame);

    // Create a scope to call into Julia
    julia
        .scope(|mut frame| {
            // Create a new two-dimensional Julia array from a `Vec`.
            let data = vec![1u64, 2u64, 3u64, 4u64];
            let arr = Array::from_vec(frame.as_extended_target(), data, (2, 2))?
                .into_jlrs_result()?;

            // Sum all the elements in the array. Calling a Julia function is unsafe
            // because it can do arbitrary things. Summing the elements of this array is
            // is safe because we've just created it.
            let sum_fn = Module::base(&frame).function(&mut frame, "sum")?;
            let result = unsafe { sum_fn.call1(&mut frame, arr.as_value()) }
                .into_jlrs_result()?
                .unbox::<u64>()?;

            assert_eq!(result, 10);
            Ok(())
        })
        .unwrap();
}

```

[Docs.rs](https://docs.rs/jlrs/)  
[Crate](https://crates.io/crates/jlrs)  
[Github](https://github.com/Taaitaaiger/jlrs)
