Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best coding practices with current objects #15

Closed
bvssvni opened this issue Nov 4, 2014 · 0 comments
Closed

Best coding practices with current objects #15

bvssvni opened this issue Nov 4, 2014 · 0 comments

Comments

@bvssvni
Copy link
Member

bvssvni commented Nov 4, 2014

Notice! piston-current is an experimental library and should be used with caution!

This library is intended to 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

Piston-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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant