jlrs
jlrs copied to clipboard
Create a website with a tutorial
The only information that's currently available is contained within the docs. It's hard to provide a step-by-step tutorial that progressively explains how to use jlrs that way, a website and a tutorial is needed.
Summary of plans:
Introduction
The first few tutorials should focus on introducing important concepts in jlrs. I think it's best to start with embedding Rust using the sync runtime because that involves the least amount of prerequisites, and using the same structure as the Embedding Julia doc does.
Sync runtime
- Create a new binary crate, add jlrs to deps, introduce version features and sync-rt feature
- Init sync runtime, explain that
StackFrame
is necessary to make Julia aware of data that is in use from Rust - Explain that Julia can't be used arbitrarily, but that all interactions with Julia must happen in a scope
- Create a scope, call
Value::eval("println(sqrt(2)")
Primitive data
- Introduce
Value
- Show that primitive types like
u8
andf64
can be converted to Julia withValue::new
, and that they can be unboxed withValue::unbox
to convert them to Rust - Explain that primitive types are immutable
Modules
- Show how Julia's module system can be accessed from Rust
- Show how to load and access installed packages
- Show how to create constants, and that some methods have two variants: one that catches exceptions, and one that doesn't
Functions
- Introduce
Call
trait - Access the
+
function and call it - Show that exceptions are caught and returned by calling
+
with incompatible arguments - Introduce
named_tuple
andWithKeywords
- Call a function with keyword arguments
Memory management
- Explain that the frame provided to a scope is used to make the GC aware of Julia data used in that scope
- In particular, explain the concept of rooting
- Explain that a
Value
has ascope
lifetime that ensures it can't be returned from the scope that roots it - Introduce targets, nested scopes, and
ExtendedTarget
- Explain there are rooting and non-rooting targets, and when it's okay to use a non-rooting target
- Show how to write functions that take a target generically, either directly or through an
ExtendedTarget
Arrays I
- Introduce
Array
, explain that it doesn't express the element type or rank at a type level - Show that an
Array
can be created from aVec
as long as the element type of theVec
implementsIntoJulia
- Show that an
Array
can be created from a slice as long as the element type of the slice implementsIntoJulia
, introduce the'data
lifetime - Explain that the content of an array can be accessed from Rust, but how this must be done depends on the element type
- Introduce the inline and value layouts of arrays, ignore the details of the bits-union layout optimization but acknowledge its existence
- Introduce tracking and its limitations
Async runtime
After the introductory material, I think it's best to introduce async runtime before diving into more complex topics.
- Explain what features are supported by the async runtime
- Show how to initialize the async runtime
- Introduce
CallAsync
- Provide examples of blocking, async and persistent tasks
Data
These tutorials dive deeper into topics like Julia types and the layouts of their instances. This information is useful if you want to access or mutate the content of Julia data directly from Rust.
Types and layouts
- Introduce
DataType
,UnionAll
,Union
,TypeName
, andTypeVar
. - Provide some information about the layout of a type.
- Introduce type constructors
- Introduce
DataType::instantiate
Values
- Provide information about the functionality provided by
Value
. - Mutate a
Value
, introduce write barrier - Introduce casting and explain how it differs from unboxing
- Introduce
FieldAccessor
Arrays II
- Introduce
TypedArray
- Introduce bits-union layout optimization
- Introduce
jlrs-ndarray
feature
Other types
- Introduce JuliaString and Symbol
- Explain that there are more types, most of them only available when the
internal-types
feature is enabled
Jlrs.Reflect
- Introduce the functionality of
JlrsReflect
- Provide information about the derived traits, but don't go in-depth about
CCallArg
andCCallReturn
- Introduce compatible types
CCall
The final set of tutorials are about calling Rust from Julia, they should focus on the julia_module
macro and the functionality that can be exposed to Julia with it.
CCall
- Introduce Julia's
ccall
interface - Explain that this interface can call
extern "C"
functions defined in Rust - Explain that the crate type must be
cdylib
to create a library that can be loaded by Julia - Introduce the
CCall
type, explain that it's essentially a sync runtime - Introduce leaking
julia_module
- Introduce
julia_module
macro and explain what can be exported with it - Explain that the
CCallArg
andCCallReturn
traits are used to generate the function and ccall argument and return types - Introduce the
RustResult
type, explain why functions called throughccall
should never throw an uncaught exception
Constants and globals
- Show that
static
andconst
global data that implementsIntoJulia
can be exported as either a module constant or global
Freestanding functions, opaque types, and their methods
- Show that
unsafe extern "C"
functions can be exported - Introduce
TypedValue
- Show how to return a
RustResult
from an exported function - Introduce
OpaqueType
trait, explain that it can be used to expose Rust data to Julia - Show that methods of opaque types can be exported similarly to
unsafe extern "C"
functions - Explain and show that methods of opaque types can take
&self
or&mut self
Foreign types and their methods
- Introduce the
ForeignType
trait, explain its similarities to and differences fromOpaqueType
- Show how to correctly mark and mutate Julia data referenced by foreign types
Async function calls
- Explain that long-running ccall'ed functions that don't allocate Julia data can prevent the GC from running
- Introduce the
AsyncCCall
trait
JLL packages
- Provide an example recipe for Yggdrassil