Skip to content

Best coding practices with current objects #15

Closed
@bvssvni

Description

@bvssvni

Notice! This library requires unsafe code and should be used with caution!

Example project: Sea Birds' Breakfast

This library is intended for two different use cases:

  • Game prototyping - where there the application structure is large and hard to refactor
  • High level libraries - that uses current objects behind a safe interface, similarly to thread locals

Current is convenient for game prototyping because it is easy to refactor between references and current objects, thereby saving the amount of rewriting of function signatures, but in such cases caution is required and one is expected to follow the safety guidelines.

High level libraries is still at an early phase of experimenting and it not recommended as a library design pattern yet.

Safety guidelines

Unsafe version (for game prototyping only):

unsafe fn current_window() -> Current<Window> { Current::new() }

Safe version (this is recommended):

pub fn current_window() -> Rc<RefCell<Window>> {
    unsafe {
        Current::<Rc<RefCell<Window>>>::new().clone()
    }
}

When you want to use the current object in a function, you do this:

let window_guard = CurrentGuard::new(&mut window); // make window current object
start(); // function that uses the current object.
drop(window_guard); // put back old current object

For the safe version, use Rc<RefCell<T>> instead of T. Example project start_piston.

Inside the function where you use the current object, you can call the function and use it as an object:

Unsafe version:

fn start() {
    use current_window;

    let window = unsafe { &mut *current_window() };
    window.set_title("Hello");
    ...
}

For the unsafe version only keep one mutable pointer in scope. Failing to do so can lead to undefined behavior.

Safe version:

fn start() {
    use current_window;

    let window = current_window();
    let mut window = window.borrow_mut();
    window.set_title("Hello");
    ...
}

Unsafe use of dereference

Dereferencing, borrowing and then assigning with unsafe current objects multiple times in same scope is dangerous. For example, the following prints out "bar":

let foo = & *current_window();
let bar = &mut *current_window();
bar.set_mut(Title("bar".to_string()));
let Title(text) = foo.get();
println!("{}", text);

It is also dangerous to call functions with references to current objects that uses the same current objects.

let foo = &mut *current_window();
bar(foo); // `bar` should not use `current_window`

This can lead to unexpected behavior under target specific compiler optimization and might not be caught by testing. Therefore, always use the safe version in libraries.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions