├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples └── text.rs └── src └── lib.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bvssvni 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.old 8 | *.bak 9 | *.kate-swp 10 | *.dylib 11 | *.dSYM 12 | *.dll 13 | *.rlib 14 | *.dummy 15 | *.exe 16 | *-test 17 | /bin/main 18 | /bin/test-internal 19 | /bin/test-external 20 | /doc/ 21 | /target/ 22 | /build/ 23 | /.rust/ 24 | rusti.sh 25 | watch.sh 26 | /examples/** 27 | !/examples/*.rs 28 | !/examples/assets/ 29 | !/bin/assets/ 30 | 31 | Cargo.lock 32 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "current" 4 | version = "1.0.1" 5 | authors = [ 6 | "bvssvni ", 7 | "Jonathan Reem " 8 | ] 9 | keywords = ["globals", "singleton", "current", "piston"] 10 | description = "A library for setting current values for stack scope, such as application structure" 11 | license = "MIT" 12 | readme = "README.md" 13 | repository = "https://github.com/pistondevelopers/current.git" 14 | homepage = "https://github.com/pistondevelopers/current" 15 | edition = "2018" 16 | 17 | [lib] 18 | 19 | name = "current" 20 | path = "src/lib.rs" 21 | 22 | [features] 23 | default = [] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Current 2 | A library for setting current values for stack scope, such as application structure 3 | 4 | Current objects are put on a shadow stack for easy access by type. 5 | The type is used as an identifier to get the latest current object in scope. 6 | 7 | This is used as a better alternative in Rust than mutable globals. 8 | 9 | There are two objects in this library: 10 | 11 | - `CurrentGuard` is used to create a current object using a mutable reference 12 | - `Current` is used to access the reference by type 13 | 14 | ### How to use it 15 | 16 | See [Best coding practices with current objects](https://github.com/PistonDevelopers/current/issues/15) 17 | -------------------------------------------------------------------------------- /examples/text.rs: -------------------------------------------------------------------------------- 1 | extern crate current; 2 | 3 | use current::{ Current, CurrentGuard }; 4 | 5 | pub struct Foo { 6 | text: String 7 | } 8 | 9 | fn print_foo() { 10 | let foo = unsafe { &*Current::::new() }; 11 | println!("{}", foo.text); 12 | let foo = unsafe { &mut *Current::::new() }; 13 | foo.text = "world!".to_string(); 14 | } 15 | 16 | fn bar() { 17 | let mut bar = Foo { text: "good bye".to_string() }; 18 | let guard = CurrentGuard::new(&mut bar); 19 | print_foo(); 20 | print_foo(); 21 | drop(guard); 22 | } 23 | 24 | fn main() { 25 | let mut foo = Foo { text: "hello".to_string() }; 26 | { 27 | let guard = CurrentGuard::new(&mut foo); 28 | print_foo(); 29 | print_foo(); 30 | bar(); 31 | drop(guard); 32 | } 33 | foo.text = "hi!".to_string(); 34 | } 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![deny(missing_docs)] 3 | 4 | use std::cell::RefCell; 5 | use std::any::{ TypeId, Any }; 6 | use std::collections::HashMap; 7 | use std::collections::hash_map::Entry::{ Occupied, Vacant }; 8 | use std::ops::{ Deref, DerefMut }; 9 | use std::marker::PhantomData; 10 | 11 | // Stores the current pointers for concrete types. 12 | thread_local!(static KEY_CURRENT: RefCell> 13 | = RefCell::new(HashMap::new())); 14 | 15 | /// Puts back the previous current pointer. 16 | pub struct CurrentGuard<'a, T> where T: Any { 17 | _val: &'a mut T, 18 | old_ptr: Option 19 | } 20 | 21 | #[allow(trivial_casts)] 22 | impl<'a, T> CurrentGuard<'a, T> where T: Any { 23 | /// Creates a new current guard. 24 | pub fn new(val: &mut T) -> CurrentGuard { 25 | let id = TypeId::of::(); 26 | let ptr = val as *mut T as usize; 27 | let old_ptr = KEY_CURRENT.with(|current| { 28 | match current.borrow_mut().entry(id) { 29 | Occupied(mut entry) => Some(entry.insert(ptr)), 30 | Vacant(entry) => { 31 | entry.insert(ptr); 32 | None 33 | } 34 | } 35 | }); 36 | CurrentGuard { old_ptr: old_ptr, _val: val } 37 | } 38 | } 39 | 40 | impl<'a, T> Drop for CurrentGuard<'a, T> where T: Any { 41 | fn drop(&mut self) { 42 | let id = TypeId::of::(); 43 | match self.old_ptr { 44 | None => { 45 | KEY_CURRENT.with(|current| { 46 | current.borrow_mut().remove(&id); 47 | }); 48 | return; 49 | } 50 | Some(old_ptr) => { 51 | KEY_CURRENT.with(|current| { 52 | match current.borrow_mut().entry(id) { 53 | Occupied(mut entry) => { entry.insert(old_ptr); } 54 | Vacant(entry) => { entry.insert(old_ptr); } 55 | }; 56 | }); 57 | } 58 | }; 59 | } 60 | } 61 | 62 | /// The current value of a type. 63 | pub struct Current(PhantomData); 64 | 65 | impl Current where T: Any { 66 | /// Creates a new current object 67 | pub unsafe fn new() -> Current { Current(PhantomData) } 68 | 69 | /// Gets mutable reference to current object. 70 | /// Requires mutable reference to prevent access to globals in safe code, 71 | /// and to prevent mutable borrows of same value in scope. 72 | /// Is unsafe because returned reference inherits lifetime from argument. 73 | pub unsafe fn current(&mut self) -> Option<&mut T> { 74 | use std::mem::transmute; 75 | let id = TypeId::of::(); 76 | let ptr: Option = KEY_CURRENT.with(|current| { 77 | current.borrow().get(&id).map(|id| *id) 78 | }); 79 | let ptr = match ptr { None => { return None; } Some(x) => x }; 80 | Some(unsafe { transmute(ptr as *mut T) }) 81 | } 82 | 83 | /// Unwraps mutable reference to current object, 84 | /// but with nicer error message. 85 | pub unsafe fn current_unwrap(&mut self) -> &mut T { 86 | fn report_unstable() -> ! { 87 | use std::any::type_name; 88 | panic!("No current `{}` is set", type_name::()); 89 | } 90 | 91 | match unsafe { self.current() } { 92 | None => report_unstable::(), 93 | Some(x) => x 94 | } 95 | } 96 | } 97 | 98 | impl Deref for Current where T: Any { 99 | type Target = T; 100 | 101 | #[inline(always)] 102 | fn deref<'a>(&'a self) -> &'a T { 103 | // This uses a custom version for immutable references 104 | // to avoid undefined behavior. 105 | 106 | unsafe fn current<'a, T: 'static>(_: &'a Current) -> Option<&'a T> { 107 | use std::mem::transmute; 108 | let id = TypeId::of::(); 109 | let ptr: Option = KEY_CURRENT.with(|current| { 110 | current.borrow().get(&id).map(|id| *id) 111 | }); 112 | let ptr = match ptr { None => { return None; } Some(x) => x }; 113 | Some(unsafe { transmute(ptr as *const T) }) 114 | } 115 | 116 | fn report_unstable() -> ! { 117 | use std::any::type_name; 118 | panic!("No current `{}` is set", type_name::()); 119 | } 120 | 121 | match unsafe { current(self) } { 122 | None => report_unstable::(), 123 | Some(x) => x 124 | } 125 | } 126 | } 127 | 128 | impl DerefMut for Current where T: Any { 129 | #[inline(always)] 130 | fn deref_mut<'a>(&'a mut self) -> &'a mut T { 131 | unsafe { self.current_unwrap() } 132 | } 133 | } 134 | --------------------------------------------------------------------------------