How to avoid self-referential struct in this use case?

Here's a rundown of my situation:

  • I am creating a library that previously used the llvm-sys crate.
  • I am using the crate to provide dynamic code generation and JIT compilation.
  • I want to replace my usage of this crate with inkwell, a type-safe wrapper around the -sys crate.
  • In both crates, there is the concept of a 'context', an object which makes available many other features.
  • The context is used to create a 'module', which is where any code that will be dynamically compiled should be placed.
  • The context cannot be destroyed before the module.
  • Considering all these things, I want to create a struct that can store and execute a dynamically generated program. Using the -sys crate, my code looked something like this:
 fn create_program() -> ProgramData {
    let context: LLVMContextRef = unsafe { LLVMCreateContext() };
    let module: LLVMModuleRef = unsafe { LLVMCreateModuleInContext(context) };
    // Add code to the module...
    // Run optimization passes...
    ProgramData::new(context, module)
}

struct ProgramData {
    context: LLVMContextRef,
    module: LLVMModuleRef,
    executor: LLVMExecutorRef,
}

impl ProgramData {
    fn new(context: LLVMContextRef, module: LLVMModuleRef) -> Self {
        let executor = unsafe { LLVMCreateExecutor(context, module) };
        Self { context, module, executor }
    }

    fn execute(&self) -> i32 {
        unsafe { self.executor.execute() }
    }
}

impl Drop for ProgramData {
    fn drop(&mut self) {
        unsafe {
            LLVMDestroyExecutor(self.executor);
            LLVMDestroyModule(self.module);
            LLVMDestroyContext(self.context);
        }
    }
}

The safe wrapper inkwell uses a lifetime parameter to ensure that the module only lives as long as the context was borrowed to create it, making create_program look something like this:

fn create_program() -> ProgramData {
    let context: Context = create_context();
    let context_ref: &'ctx Context = &context;
    let module: Module<'ctx> = context.create_module();
    // Do module stuff...
    unimplemented!();
}

The problem now is that the ProgramData struct needs to be self-referential. It needs to contain the context as well as a struct which refers to the lifetime of a reference to t

1 Like

One way is to expose similarly-shaped abstraction in your code, and have crate_program that returns context, and then create_program_context function that makes ProgramContext<'ctx> that has context_ref and module in it.

The other way, if you can ensure that context and the other two are going to be kept together, is to use unsafe to override their lifetimes.

There are some crates that create safer API around self-referential types: