### Cargo.toml Setup for jlrs Source: https://context7.com/taaitaaiger/jlrs/llms.txt Configure your Cargo.toml to include jlrs with the appropriate runtime feature flag for embedding Julia or creating libraries callable from Julia. For libraries called from Julia, ensure `crate-type` is set to `cdylib`. ```toml # Embedding Julia in a Rust application (choose one runtime): [dependencies] jlrs = { version = "0.23", features = ["local-rt"] } # single-threaded #jlrs = { version = "0.23", features = ["tokio-rt"] } # async with Tokio #jlrs = { version = "0.23", features = ["multi-rt"] } # call from multiple threads # For libraries called from Julia (no runtime, uses ccall interface): #jlrs = { version = "0.23", features = ["ccall", "jlrs-derive"] } # Enable all non-runtime utilities: #jlrs = { version = "0.23", features = ["full-no-rt"] } # Required for cdylib libraries called from Julia: [lib] crate-type = ["cdylib"] [profile.release] panic = "abort" ``` -------------------------------- ### Module::base(), Module::main(), Module::core() — Access Julia Modules Source: https://context7.com/taaitaaiger/jlrs/llms.txt Static accessors for Julia's built-in root modules. Use `global()` to retrieve functions and values, `submodule()` to traverse the module hierarchy, and `package_root_module()` to access installed packages. ```APIDOC ## `Module::base()`, `Module::main()`, `Module::core()` — Access Julia Modules Static accessors for Julia's built-in root modules. Use `global()` to retrieve functions and values, `submodule()` to traverse the module hierarchy, and `package_root_module()` to access installed packages. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); // Load a standard library package first unsafe { julia.using("LinearAlgebra").expect("package not installed") }; julia.local_scope::<_, 4>(|mut frame| -> JlrsResult<()> { // Base module: arithmetic, I/O, etc. let sqrt = Module::base(&frame).global(&mut frame, "sqrt")?; let arg = Value::new(&mut frame, 4.0f64); let result = unsafe { sqrt.call(&frame, [arg])? }; assert!((result.unbox::()? - 2.0).abs() < 1e-10); // Main module: user-defined code lives here unsafe { Value::eval_string(&frame, "MY_CONST = 42")? }; let val = Module::main(&frame).global(&mut frame, "MY_CONST")?; assert_eq!(val.unbox::()?, 42); // Access an installed package by name let lin_alg = Module::package_root_module(&frame, "LinearAlgebra") .expect("LinearAlgebra not loaded"); let _mul = lin_alg.global(&mut frame, "mul!")?; Ok(()) }).unwrap(); } ``` ``` -------------------------------- ### Programmatically Configure JlrsCore Version in Rust Source: https://context7.com/taaitaaiger/jlrs/llms.txt Configure the JlrsCore version programmatically using the Builder pattern in Rust. This example sets JlrsCore to version 0.5.0. ```rust // Or configure programmatically via Builder: use jlrs::{InstallJlrsCore, prelude::*}; fn main() { let mut julia = Builder::new() .install_jlrs(InstallJlrsCore::Version { major: 0, minor: 5, patch: 0 }) .start_local() .unwrap(); // Julia initializes with JlrsCore 0.5.0 let _ = julia; } ``` -------------------------------- ### Set JlrsCore Version via Environment Variables Source: https://context7.com/taaitaaiger/jlrs/llms.txt Control the JlrsCore Julia package version at runtime using environment variables. Supports specific versions, git revisions, or skipping installation. Priority: NO_INSTALL > REVISION > VERSION > default. ```bash # Install and use a specific JlrsCore version JLRS_CORE_VERSION=0.5.0 ./my_app ``` ```bash # Use a specific git revision (e.g., from a fork) JLRS_CORE_REPO=https://github.com/myfork/JlrsCore.jl \ JLRS_CORE_REVISION=abc123 ./my_app ``` ```bash # Skip installation entirely (JlrsCore must already be installed) JLRS_CORE_NO_INSTALL=1 ./my_app ``` -------------------------------- ### Load Custom Julia System Image Source: https://context7.com/taaitaaiger/jlrs/llms.txt Reduce Julia startup time by loading a precompiled system image using `Builder::image()`. Ensure the provided bindir and sysimage paths are valid. This method is unsafe and requires careful handling of paths. ```rust use jlrs::prelude::*; use std::path::PathBuf; fn main() { let bindir = PathBuf::from("/usr/local/julia/bin"); let sysimage = PathBuf::from("/path/to/custom_sysimage.so"); let mut julia = unsafe { Builder::new() .image(bindir, sysimage) .expect("image paths do not exist") .start_local() .expect("Could not initialize Julia") }; julia.local_scope::<_, 1>(|mut frame| { // All code compiled into the sysimage is immediately available let _v = Value::new(&mut frame, 1.0f64); }); } ``` -------------------------------- ### Initialize Multithreaded Julia Runtime Source: https://context7.com/taaitaaiger/jlrs/llms.txt Use `Builder::start_mt` to initialize Julia for multithreaded access. Each thread uses `MtHandle::with` to safely interact with Julia, preventing GC stalls. ```rust use std::thread; use jlrs::prelude::*; fn main() { Builder::new() .n_threads(4) .start_mt(|mt_handle| { // Spawn multiple threads that each call into Julia let t1 = mt_handle.spawn(|mut h| { h.with(|handle| { handle.local_scope::<_, 1>(|mut frame| unsafe { Value::new(&mut frame, 1i64).unbox::().unwrap() }) }) }); let t2 = mt_handle.spawn(|mut h| { h.with(|handle| { handle.local_scope::<_, 1>(|mut frame| unsafe { Value::new(&mut frame, 2i64).unbox::().unwrap() }) }) }); assert_eq!(t1.join().unwrap(), 1); assert_eq!(t2.join().unwrap(), 2); }) .unwrap(); } ``` -------------------------------- ### Include Julia Source Files in Rust Source: https://context7.com/taaitaaiger/jlrs/llms.txt Demonstrates how to load and execute Julia source files (`.jl`) from Rust using `Runtime::include()`. Definitions from the included file become accessible via `Module::main`. ```rust use jlrs::prelude::*; use std::path::PathBuf; fn main() { let mut julia = Builder::new().start_local().unwrap(); // Include a Julia source file (unsafe: runs arbitrary Julia code) unsafe { julia.include("src/MyModule.jl").expect("failed to include file"); } julia.local_scope::<_, 2>(|mut frame| -> JlrsResult<()> { // Access definitions from the included file let module = Module::main(&frame) .submodule(&frame, "MyModule")?; .as_managed(); let func = module.global(&mut frame, "my_function")?; let arg = Value::new(&mut frame, 42isize); let result = unsafe { func.call(&frame, [arg])? }; println!("Result: {:?}", result.unbox::()); Ok(()) }).unwrap(); } ``` -------------------------------- ### Initialize Local Julia Runtime Source: https://context7.com/taaitaaiger/jlrs/llms.txt Use `Builder::new().start_local()` to initialize Julia on the current thread. The returned `LocalHandle` manages the Julia runtime, which shuts down when the handle is dropped. You can optionally configure the number of Julia threads. ```rust use jlrs::prelude::*; fn main() { // Configure and start Julia on the current thread let mut julia = Builder::new() .n_threads(4) // optional: set Julia thread count .start_local() .expect("Could not initialize Julia"); // `julia` is a LocalHandle; Julia shuts down when it's dropped julia.local_scope::<_, 1>(|mut frame| { let v = Value::new(&mut frame, 42u64); let result = v.unbox::().expect("not a UInt64"); assert_eq!(result, 42); }); } ``` -------------------------------- ### Access Julia Modules with `Module::base()`, `Module::main()`, `Module::core()` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Shows how to access Julia's root modules (Base, Main, Core) to retrieve functions and values using `global()`. It also demonstrates loading and accessing a package module. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); // Load a standard library package first unsafe { julia.using("LinearAlgebra").expect("package not installed") }; julia.local_scope::<_, 4>(|mut frame| -> JlrsResult<()> { // Base module: arithmetic, I/O, etc. let sqrt = Module::base(&frame).global(&mut frame, "sqrt")?; let arg = Value::new(&mut frame, 4.0f64); let result = unsafe { sqrt.call(&frame, [arg])? }; assert!((result.unbox::()? - 2.0).abs() < 1e-10); // Main module: user-defined code lives here unsafe { Value::eval_string(&frame, "MY_CONST = 42")? }; let val = Module::main(&frame).global(&mut frame, "MY_CONST")?; assert_eq!(val.unbox::()?, 42); // Access an installed package by name let lin_alg = Module::package_root_module(&frame, "LinearAlgebra") .expect("LinearAlgebra not loaded"); let _mul = lin_alg.global(&mut frame, "mul!")?; Ok(()) }).unwrap(); } ``` -------------------------------- ### Convert Julia Values to Rust with `Value::unbox()` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Shows how to safely extract Rust values from Julia `Value`s using `unbox()`. It includes type checking and demonstrates unboxing after a Julia computation. Incorrect types result in an error. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 2>(|mut frame| -> JlrsResult<()> { // Round-trip: Rust → Julia → Rust let julia_val = Value::new(&mut frame, 255u8); // Safe unbox with type check let rust_val = julia_val.unbox::().expect("not a UInt8"); assert_eq!(rust_val, 255u8); // Wrong type returns an error let err = julia_val.unbox::(); assert!(err.is_err()); // Unbox after a Julia computation let sum = unsafe { Module::base(&frame) .global(&mut frame, "+")? .call(&frame, [julia_val, julia_val])? }; assert_eq!(sum.unbox::()?, 254u8); // wraps around Ok(()) }).unwrap(); } ``` -------------------------------- ### Create and Manage Julia Arrays in Rust Source: https://context7.com/taaitaaiger/jlrs/llms.txt Illustrates creating, writing to, and reading from Julia arrays using `TypedArray`. It also shows how to move a Rust `Vec` into a Julia array and call Julia's `sum` function on it. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { // Create a new Julia-managed 1-D array of f64 let arr: TypedArray = TypedArray::::new(&mut frame, 5)?; // Write data unsafe { let mut data = arr.bits_data_mut(); for i in 0..5 { data[i] = (i as f64) * 2.0; } } // Read data back let data = unsafe { arr.bits_data() }; assert_eq!(data[2], 4.0); // Move a Vec from Rust to Julia let rust_vec = vec![1.0f64, 2.0, 3.0, 4.0]; let from_vec: TypedArray = TypedArray::from_vec(&mut frame, rust_vec, 4)??; // Pass to Julia's `sum` function let sum_fn = Module::base(&frame).global(&mut frame, "sum")?; let total = unsafe { sum_fn.call(&frame, [from_vec.as_value()])? }; assert_eq!(total.unbox::()?, 10.0); Ok(()) }).unwrap(); } ``` -------------------------------- ### Call Julia Functions with Keyword Arguments Source: https://context7.com/taaitaaiger/jlrs/llms.txt Shows how to call Julia functions with keyword arguments using `call_kw`. Keyword arguments are passed via a `NamedTuple` constructed with the `named_tuple!` macro. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 5>(|mut frame| -> JlrsResult<()> { // Define a function with keyword arguments unsafe { Value::eval_string(&frame, "kwfunc(x; scale=1, offset=0) = x * scale + offset")?; } let x = Value::new(&mut frame, 10isize); let scale_val = Value::new(&mut frame, 3isize); let offset_val = Value::new(&mut frame, 5isize); // Build the NamedTuple for keyword arguments let kws = named_tuple!(&mut frame, "scale" => scale_val, "offset" => offset_val).unwrap(); let func = Module::main(&frame).global(&mut frame, "kwfunc")?; let result = unsafe { func.call_kw(&frame, [x], kws)? }; assert_eq!(result.unbox::()?, 35); // 10 * 3 + 5 Ok(()) }).unwrap(); } ``` -------------------------------- ### Implement Stateful Persistent Tasks Source: https://context7.com/taaitaaiger/jlrs/llms.txt Define a `PersistentTask` to manage state across multiple calls. The `init` method sets up the initial state, and `run` executes logic using that state and provided input. ```rust use jlrs::prelude::*; struct CounterTask; struct CounterState<'s> { array: TypedArray<'s, 'static, i64>, count: usize } impl PersistentTask for CounterTask { type Output = JlrsResult; type State<'s> = CounterState<'s>; type Input = i64; async fn init<'frame>(&mut self, mut frame: AsyncGcFrame<'frame>) -> JlrsResult> { let arr = TypedArray::::new(&mut frame, 10)?; Ok(CounterState { array: arr, count: 0 }) } async fn run<'frame, 'state: 'frame>( &mut self, mut frame: AsyncGcFrame<'frame>, state: &mut Self::State<'state>, input: i64, ) -> Self::Output { unsafe { state.array.bits_data_mut()[state.count] = input; } state.count += 1; let sum_fn = Module::base(&frame).global(&mut frame, "sum")?; unsafe { sum_fn.call(&frame, [state.array.as_value()])?.unbox::() } } } fn main() { let (julia, handle) = Builder::new().async_runtime(Tokio::<2>::new(false)).spawn().unwrap(); let task = julia.persistent(CounterTask).try_dispatch().unwrap() .blocking_recv().unwrap().unwrap(); let r1 = task.call(10).try_dispatch().unwrap().blocking_recv().unwrap().unwrap(); assert_eq!(r1, 10); let r2 = task.call(20).try_dispatch().unwrap().blocking_recv().unwrap().unwrap(); assert_eq!(r2, 30); std::mem::drop(julia); std::mem::drop(task); handle.join().unwrap(); } ``` -------------------------------- ### Call Julia Functions with Variadic Arguments Source: https://context7.com/taaitaaiger/jlrs/llms.txt Demonstrates calling Julia's variadic '+' function with multiple arguments. Exceptions are caught and returned as an Err, while `call_unchecked` offers a faster alternative for known-safe calls. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 6>(|mut frame| -> JlrsResult<()> { // Variadic call: Julia's + accepts any number of args let add = Module::base(&frame).global(&mut frame, "+")?; let (a, b, c) = (Value::new(&mut frame, 1u32), Value::new(&mut frame, 2u32), Value::new(&mut frame, 3u32)); let sum = unsafe { add.call(&frame, [a, b, c])? }; assert_eq!(sum.unbox::()?, 6); // Exceptions are caught and returned as Err let err_result = unsafe { add.call(&frame, []) }; // + with no args throws assert!(err_result.is_err()); // call_unchecked skips try/catch (faster, but panics on exception) let fast_sum = unsafe { add.call_unchecked(&frame, [a, b]) }; assert_eq!(fast_sum.unbox::()?, 3); Ok(()) }).unwrap(); } ``` -------------------------------- ### Runtime::include() — Load Julia Source Files Source: https://context7.com/taaitaaiger/jlrs/llms.txt Include a `.jl` file relative to the `Main` module. All definitions in the file become accessible through `Module::main`. ```APIDOC ## `Runtime::include()` — Load Julia Source Files Include a `.jl` file relative to the `Main` module. All definitions in the file become accessible through `Module::main`. ### Method `include(path)` ### Parameters - `path`: A string slice representing the path to the Julia source file. ### Request Example ```rust use jlrs::prelude::*; use std::path::PathBuf; fn main() { let mut julia = Builder::new().start_local().unwrap(); // Include a Julia source file (unsafe: runs arbitrary Julia code) unsafe { julia.include("src/MyModule.jl").expect("failed to include file"); } julia.local_scope::<_, 2>(|mut frame| -> JlrsResult<()> { // Access definitions from the included file let module = Module::main(&frame) .submodule(&frame, "MyModule")?; .as_managed(); let func = module.global(&mut frame, "my_function")?; let arg = Value::new(&mut frame, 42isize); let result = unsafe { func.call(&frame, [arg])? }; println!("Result: {:?}", result.unbox::()); Ok(()) }).unwrap(); } ``` ### Response - `Result<(), JlrsError>`: Indicates success or failure of the include operation. ``` -------------------------------- ### Export Rust Functions and Types to Julia with `julia_module!` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Use the `julia_module!` macro to export Rust functions, structs, and methods to Julia. Ensure your crate is compiled with `cdylib`. ```rust use jlrs::prelude::*; fn add_numbers(a: f64, b: f64) -> f64 { a + b } #[derive(OpaqueType)] struct Counter { value: i64, } impl Counter { fn new(start: i64) -> Self { Counter { value: start } } fn increment(&mut self) -> i64 { self.value += 1; self.value } fn get(&self) -> i64 { self.value } } julia_module! { become mylib_init; // name of the init function Julia calls fn add_numbers(a: f64, b: f64) -> f64; struct Counter; in Counter fn new(start: i64) -> Counter; in Counter fn increment(&mut self) -> i64; in Counter fn get(&self) -> i64; } ``` ```julia module MyLib using JlrsCore.Wrap @wrapmodule("./libmylib.so", :mylib_init) function __init__() @initjlrs end end # Usage: c = MyLib.Counter(0) MyLib.increment(c) # => 1 MyLib.get(c) # => 1 MyLib.add_numbers(1.5, 2.5) # => 4.0 ``` -------------------------------- ### Rust Integration Test Structure for JlrsCore Source: https://context7.com/taaitaaiger/jlrs/llms.txt Demonstrates a testing pattern for Rust integration tests involving JlrsCore. Julia can only be initialized once per process, so use a single `#[test]` function per file to initialize Julia and dispatch to helper functions. ```rust // tests/integration_test.rs use jlrs::{prelude::*, runtime::handle::local_handle::LocalHandle}; fn test_addition(julia: &mut LocalHandle) { julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { let a = Value::new(&mut frame, 1.0f64); let b = Value::new(&mut frame, 2.0f64); let add = Module::base(&frame).global(&mut frame, "+")?; let result = unsafe { add.call(&frame, [a, b])?.unbox::()? }; assert_eq!(result, 3.0); Ok(()) }).unwrap(); } fn test_string_ops(julia: &mut LocalHandle) { julia.local_scope::<_, 2>(|mut frame| -> JlrsResult<()> { let s = JuliaString::new(&mut frame, "hello"); assert_eq!(s.as_str()?, "hello"); Ok(()) }).unwrap(); } // ONE test function per file initializes Julia #[test] fn all_tests() { let mut julia = Builder::new().start_local().unwrap(); test_addition(&mut julia); test_string_ops(&mut julia); } ``` -------------------------------- ### Convert Rust Primitives to Julia Values with `Value::new()` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Demonstrates converting various Rust primitive types (integers, floats, booleans, characters, pointers) into Julia `Value`s. These values are rooted in the current frame. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 5>(|mut frame| { let i: Value = Value::new(&mut frame, 42i64); let f: Value = Value::new(&mut frame, 3.14f64); let b: Value = Value::new(&mut frame, true); let c: Value = Value::new(&mut frame, 'A'); let ptr: Value = Value::new(&mut frame, std::ptr::null_mut::()); assert_eq!(i.unbox::().unwrap(), 42); assert!((f.unbox::().unwrap() - 3.14).abs() < 1e-10); assert_eq!(b.unbox::().unwrap(), true); }); } ``` -------------------------------- ### Run Async Tasks with Tokio Executor Source: https://context7.com/taaitaaiger/jlrs/llms.txt Use `Builder::async_runtime` with a Tokio executor to run Julia tasks on a background thread. Submit blocking, async, or persistent tasks. Tokio capacity is specified by `Tokio::`. ```rust use jlrs::prelude::*; struct ComputeTask { a: f64, b: f64 } impl AsyncTask for ComputeTask { type Output = JlrsResult; async fn run<'frame>(self, mut frame: AsyncGcFrame<'frame>) -> Self::Output { let a = Value::new(&mut frame, self.a); let b = Value::new(&mut frame, self.b); let add = Module::base(&frame).global(&mut frame, "+")?; // call_async schedules on a Julia thread, freeing the runtime for other tasks unsafe { add.call_async(&mut frame, [a, b]) } .await? .unbox::() } } fn main() { // Tokio:: — N is the channel capacity let (julia, thread_handle) = Builder::new() .async_runtime(Tokio::<4>::new(false)) .spawn() .expect("Could not init Julia"); let receiver = julia .task(ComputeTask { a: 1.5, b: 2.5 }) .try_dispatch() .expect("channel full"); let result = receiver.blocking_recv().unwrap().unwrap(); assert_eq!(result, 4.0); // Blocking task: runs atomically, no other tasks execute until done let blocking_rx = julia .blocking_task(|mut frame| -> JlrsResult { Value::new(&mut frame, 99i64).unbox::() }) .try_dispatch() .unwrap(); assert_eq!(blocking_rx.blocking_recv().unwrap().unwrap(), 99); std::mem::drop(julia); thread_handle.join().unwrap(); } ``` -------------------------------- ### FFI Validator CLI Usage Source: https://github.com/taaitaaiger/jlrs/blob/master/crates/ffi/ffi-validator/README.md This shows the command-line interface for the FFI validator. It requires paths to the `jl_sys` and `jlrs_sys` bindings. ```bash Usage: ffi-validator [OPTIONS] Arguments: Options: -p, --print-types Print all types used by the bindings and exit -h, --help Print help -V, --version Print version ``` -------------------------------- ### `julia_module!` Macro Source: https://context7.com/taaitaaiger/jlrs/llms.txt The `julia_module!` macro is the primary mechanism for creating Julia libraries from Rust. It exports functions, types, and methods so Julia can call them via `ccall` with automatic wrapper generation. ```APIDOC ## `julia_module!` Macro — Export Rust to Julia The primary mechanism for creating Julia libraries from Rust. Exports functions, types, and methods so Julia can call them via `ccall` with automatic wrapper generation using `JlrsCore.Wrap`. ```rust // lib.rs (crate-type = ["cdylib"]) use jlrs::prelude::*; fn add_numbers(a: f64, b: f64) -> f64 { a + b } #[derive(OpaqueType)] struct Counter { value: i64, } impl Counter { fn new(start: i64) -> Self { Counter { value: start } } fn increment(&mut self) -> i64 { self.value += 1; self.value } fn get(&self) -> i64 { self.value } } julia_module! { become mylib_init; // name of the init function Julia calls fn add_numbers(a: f64, b: f64) -> f64; struct Counter; in Counter fn new(start: i64) -> Counter; in Counter fn increment(&mut self) -> i64; in Counter fn get(&self) -> i64; } // Julia side (MyLib.jl): // module MyLib // using JlrsCore.Wrap // @wrapmodule("./libmylib.so", :mylib_init) // function __init__() // @initjlrs // end // end // // Usage: // c = MyLib.Counter(0) // MyLib.increment(c) // => 1 // MyLib.get(c) // => 1 // MyLib.add_numbers(1.5, 2.5) // => 4.0 ``` ``` -------------------------------- ### Evaluate Julia Code with `Value::eval_string()` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Demonstrates using `eval_string()` to define a Julia function at runtime and then calling it from Rust. This method is useful for one-off computations and defining functions dynamically. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { // Define a Julia function at runtime unsafe { Value::eval_string( &frame, "greet(name::String) = \"Hello, \" * name * \"!\"" )?; } // Call the newly defined function let name = JuliaString::new(&mut frame, "World"); let func = Module::main(&frame).global(&mut frame, "greet")?; let greeting = unsafe { func.call(&frame, [name.as_value()])? }; println!("{}", greeting.unbox::()?); // Hello, World! Ok(()) }).unwrap(); } ``` -------------------------------- ### Julia Scopes: `local_scope` and `with_stack` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Safely allocate and use Julia data within scopes. `local_scope::<_, N>` uses a statically-sized frame with `N` roots, which is preferred. `with_stack` combined with `scope` provides a dynamically-sized frame for variable slot counts. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); // Statically-sized scope (preferred): N = number of mutable frame uses julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { let a = Value::new(&mut frame, 10.0f64); // slot 1 let b = Value::new(&mut frame, 5.0f64); // slot 2 let func = Module::base(&frame).global(&mut frame, "+")?; let result = unsafe { func.call(&frame, [a, b])? }; assert_eq!(result.unbox::()?, 15.0); Ok(()) }).unwrap(); // Dynamically-sized scope (for variable slot counts) julia.with_stack(|mut stack| { stack.scope(|mut frame| { let _v = Value::new(&mut frame, "hello"); }) }); } ``` -------------------------------- ### Call::call_kw() — Call with Keyword Arguments Source: https://context7.com/taaitaaiger/jlrs/llms.txt Pass keyword arguments to a Julia function by constructing a `NamedTuple` with the `named_tuple!` macro, then calling `call_kw`. ```APIDOC ## `Call::call_kw()` — Call with Keyword Arguments Pass keyword arguments to a Julia function by constructing a `NamedTuple` with the `named_tuple!` macro, then calling `call_kw`. ### Method `call_kw` ### Parameters - `frame`: A mutable reference to the current `Frame`. - `args`: A slice of `Value` representing the positional arguments. - `kws`: A `Value` representing a `NamedTuple` of keyword arguments. ### Request Example ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 5>(|mut frame| -> JlrsResult<()> { // Define a function with keyword arguments unsafe { Value::eval_string(&frame, "kwfunc(x; scale=1, offset=0) = x * scale + offset")?; } let x = Value::new(&mut frame, 10isize); let scale_val = Value::new(&mut frame, 3isize); let offset_val = Value::new(&mut frame, 5isize); // Build the NamedTuple for keyword arguments let kws = named_tuple!(&mut frame, "scale" => scale_val, "offset" => offset_val).unwrap(); let func = Module::main(&frame).global(&mut frame, "kwfunc")?; let result = unsafe { func.call_kw(&frame, [x], kws)? }; assert_eq!(result.unbox::()?, 35); // 10 * 3 + 5 Ok(()) }).unwrap(); } ``` ### Response - `Result`: The result of the Julia function call. `Ok(Value)` on success, `Err(JlrsError)` if an exception occurred. ``` -------------------------------- ### Handle Julia Exceptions with `JlrsResult` and `JuliaResult` Source: https://context7.com/taaitaaiger/jlrs/llms.txt Jlrs uses `JlrsResult` for its own errors and `JuliaResult` (`Result`) for Julia exceptions caught by `call`. Use `?` or `.into_jlrs_result()` for conversion. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { // Julia exceptions become Err(Value) from call() let sqrt = Module::base(&frame).global(&mut frame, "sqrt")?; let neg = Value::new(&mut frame, -1.0f64); match unsafe { sqrt.call(&frame, [neg]) } { Ok(v) => println!("sqrt = {}", v.unbox::().unwrap()), Err(exc) => { // exc is the Julia exception value println!("Exception: {}", exc.display_string_or("")); } } // Using ? converts JuliaResult to JlrsResult (uses exception message as error) let pos = Value::new(&mut frame, 4.0f64); let result = unsafe { sqrt.call(&frame, [pos]) }?; assert!((result.unbox::()? - 2.0).abs() < 1e-10); Ok(()) }).unwrap(); } ``` -------------------------------- ### Value::unbox() — Convert Julia to Rust Source: https://context7.com/taaitaaiger/jlrs/llms.txt Extracts (unboxes) the Rust value from a Julia `Value`. Performs a type-check first and returns an error if the types do not match. ```APIDOC ## `Value::unbox()` — Convert Julia to Rust Extracts (unboxes) the Rust value from a Julia `Value`. Performs a type-check first and returns an error if the types do not match. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 2>(|mut frame| -> JlrsResult<()> { // Round-trip: Rust → Julia → Rust let julia_val = Value::new(&mut frame, 255u8); // Safe unbox with type check let rust_val = julia_val.unbox::().expect("not a UInt8"); assert_eq!(rust_val, 255u8); // Wrong type returns an error let err = julia_val.unbox::(); assert!(err.is_err()); // Unbox after a Julia computation let sum = unsafe { Module::base(&frame) .global(&mut frame, "+")? .call(&frame, [julia_val, julia_val])? }; assert_eq!(sum.unbox::()?, 254u8); // wraps around Ok(()) }).unwrap(); } ``` ``` -------------------------------- ### Value::new() — Convert Rust to Julia Source: https://context7.com/taaitaaiger/jlrs/llms.txt Converts any type implementing `IntoJulia` into a Julia value rooted in the current frame. Primitive types and pointer types implement `IntoJulia` by default. ```APIDOC ## `Value::new()` — Convert Rust to Julia Converts any type implementing `IntoJulia` into a Julia value rooted in the current frame. Primitive types (`i8`, `u8`, ..., `i64`, `u64`, `f32`, `f64`, `bool`, `char`, pointer types) implement `IntoJulia` by default. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 5>(|mut frame| { let i: Value = Value::new(&mut frame, 42i64); let f: Value = Value::new(&mut frame, 3.14f64); let b: Value = Value::new(&mut frame, true); let c: Value = Value::new(&mut frame, 'A'); let ptr: Value = Value::new(&mut frame, std::ptr::null_mut::()); assert_eq!(i.unbox::().unwrap(), 42); assert!((f.unbox::().unwrap() - 3.14).abs() < 1e-10); assert_eq!(b.unbox::().unwrap(), true); }); } ``` ``` -------------------------------- ### Value::eval_string() — Evaluate Julia Code Source: https://context7.com/taaitaaiger/jlrs/llms.txt Parses and evaluates an arbitrary Julia expression string. Returns the result as a `Value` or an exception. Useful for defining functions, loading packages, and one-off computations. ```APIDOC ## `Value::eval_string()` — Evaluate Julia Code Parses and evaluates an arbitrary Julia expression string. Returns the result as a `Value` or an exception. Useful for defining functions, loading packages, and one-off computations. ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { // Define a Julia function at runtime unsafe { Value::eval_string( &frame, "greet(name::String) = \"Hello, \" * name * \"!\"" )?; } // Call the newly defined function let name = JuliaString::new(&mut frame, "World"); let func = Module::main(&frame).global(&mut frame, "greet")?; let greeting = unsafe { func.call(&frame, [name.as_value()])? }; println!("{}", greeting.unbox::()?); // Hello, World! Ok(()) }).unwrap(); } ``` ``` -------------------------------- ### TypedArray / Array — Julia Arrays Source: https://context7.com/taaitaaiger/jlrs/llms.txt Create, borrow, or move n-dimensional arrays between Rust and Julia. `TypedArray` carries the element type at compile time; `Array` is untyped. Access elements via `bits_data()`, `bits_data_mut()`, or tracked accessors. ```APIDOC ## `TypedArray` / `Array` — Julia Arrays Create, borrow, or move n-dimensional arrays between Rust and Julia. `TypedArray` carries the element type at compile time; `Array` is untyped. Access elements via `bits_data()`, `bits_data_mut()`, or tracked accessors. ### Methods - `TypedArray::::new(&mut frame, dims)`: Creates a new Julia-managed n-dimensional array with element type `T` and dimensions `dims`. - `TypedArray::from_vec(&mut frame, vec, dims)`: Creates a new Julia-managed array from a Rust `Vec`. - `bits_data()`: Returns an immutable slice to the array's data. - `bits_data_mut()`: Returns a mutable slice to the array's data. ### Parameters - `frame`: A mutable reference to the current `Frame`. - `T`: The element type of the array (e.g., `f64`, `u32`). - `dims`: A slice representing the dimensions of the array. - `vec`: A Rust `Vec` containing the data for the array. ### Request Example ```rust use jlrs::prelude::*; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 3>(|mut frame| -> JlrsResult<()> { // Create a new Julia-managed 1-D array of f64 let arr: TypedArray = TypedArray::::new(&mut frame, 5)?; // Write data unsafe { let mut data = arr.bits_data_mut(); for i in 0..5 { data[i] = (i as f64) * 2.0; } } // Read data back let data = unsafe { arr.bits_data() }; assert_eq!(data[2], 4.0); // Move a Vec from Rust to Julia let rust_vec = vec![1.0f64, 2.0, 3.0, 4.0]; let from_vec: TypedArray = TypedArray::from_vec(&mut frame, rust_vec, 4)??; // Pass to Julia's `sum` function let sum_fn = Module::base(&frame).global(&mut frame, "sum")?; let total = unsafe { sum_fn.call(&frame, [from_vec.as_value()])? }; assert_eq!(total.unbox::()?, 10.0); Ok(()) }).unwrap(); } ``` ### Response - `TypedArray` or `Array`: A Julia array managed by JLRS. ``` -------------------------------- ### Control Julia's Garbage Collector with `Gc` Trait Source: https://context7.com/taaitaaiger/jlrs/llms.txt The `Gc` trait provides methods to enable/disable the GC, force collection, insert safepoints, and enter GC-safe regions for long-running non-Julia operations. ```rust use jlrs::{memory::gc::{gc_safe, Gc}, prelude::*}; use std::time::Duration; fn main() { let mut julia = Builder::new().start_local().unwrap(); julia.local_scope::<_, 1>(|mut frame| { // Disable GC temporarily (e.g., during a critical section) frame.enable_gc(false); let _v = Value::new(&mut frame, 1u64); frame.enable_gc(true); // Force a full GC collection frame.gc_collect(jlrs::memory::gc::GcCollection::Full); // Check GC state assert!(frame.gc_is_enabled()); }); // For long non-Julia work in the multithreaded runtime, use gc_safe // to allow the GC to run while blocked: // unsafe { gc_safe(|| std::thread::sleep(Duration::from_millis(100))) }; } ``` -------------------------------- ### Expose Rust Structs as Opaque or Foreign Types in Julia Source: https://context7.com/taaitaaiger/jlrs/llms.txt Use `OpaqueType` for pure Rust structs and `ForeignType` for structs holding Julia data. `ForeignType` requires a custom GC mark function. ```rust use jlrs::prelude::*; // OpaqueType: no Julia data inside — use for pure Rust structs #[derive(OpaqueType)] struct Buffer { data: Vec, } impl Buffer { fn capacity(&self) -> usize { self.data.capacity() } fn push(&mut self, byte: u8) { self.data.push(byte); } } // ForeignType: holds WeakValue references — GC mark is auto-generated #[derive(ForeignType)] struct JuliaContainer { #[jlrs(mark)] // field is marked by the GC items: Vec>>, } unsafe impl Send for JuliaContainer {} unsafe impl Sync for JuliaContainer {} impl JuliaContainer { unsafe fn store(&mut self, val: Value<'_, 'static>) { let weak = val.as_weak().leak(); self.items.push(Some(weak)); unsafe { self.write_barrier(weak, self) }; // required write barrier } } julia_module! { become container_init; struct Buffer; in Buffer fn capacity(&self) -> usize; in Buffer fn push(&mut self, byte: u8); struct JuliaContainer; in JuliaContainer fn store(&mut self, val: Value<'_, 'static>); } ```