This repository was archived by the owner on Jun 8, 2021. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 59
Add clone macro #527
Merged
Merged
Add clone macro #527
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
ad9e790
Add clone macro
antoyo f1f9aaa
Allow specifying whether it is a weak or strong reference
antoyo b43145a
Fix clone macro
antoyo c4bbb0a
Fix clone macro
antoyo 2a60265
Default value instead of panic
antoyo 2f41c31
Add tests
antoyo cea7448
Move default return value in macro
antoyo 89b3c6d
Add Downgrade and Upgrade implementation for GObjects
antoyo ec7a8b3
Fix panic in clone macro
antoyo e3e1e7c
Fix panic in clone macro
antoyo 0e18c8b
Try to fix impl for GObjects
antoyo 90eb5ec
Address various review comments for the clone! macro
sdroege 9bcacc5
Add clone! documentation, tighten the trait bounds of the clone macro…
sdroege d34d1ce
Fix trait impl conherence problems around the clone! macro traits
sdroege 7d73b51
Improve doc styling
GuillaumeGomez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| use std::rc::{self, Rc}; | ||
| use std::sync::{self, Arc}; | ||
|
|
||
| /// Trait for generalizing downgrading a strong reference to a weak reference. | ||
| pub trait Downgrade | ||
| where | ||
| Self: Sized, | ||
| { | ||
| /// Weak reference type. | ||
| type Weak; | ||
|
|
||
| /// Downgrade to a weak reference. | ||
| fn downgrade(&self) -> Self::Weak; | ||
| } | ||
|
|
||
| /// Trait for generalizing upgrading a weak reference to a strong reference. | ||
| pub trait Upgrade | ||
| where | ||
| Self: Sized, | ||
| { | ||
| /// Strong reference type. | ||
| type Strong; | ||
|
|
||
| /// Try upgrading a weak reference to a strong reference. | ||
| fn upgrade(&self) -> Option<Self::Strong>; | ||
| } | ||
|
|
||
| impl<T: Downgrade + crate::ObjectType> Upgrade for crate::WeakRef<T> { | ||
| type Strong = T; | ||
|
|
||
| fn upgrade(&self) -> Option<Self::Strong> { | ||
| self.upgrade() | ||
| } | ||
| } | ||
|
|
||
| impl<T> Downgrade for Arc<T> { | ||
| type Weak = sync::Weak<T>; | ||
|
|
||
| fn downgrade(&self) -> Self::Weak { | ||
| Arc::downgrade(self) | ||
| } | ||
| } | ||
|
|
||
| impl<T> Upgrade for sync::Weak<T> { | ||
| type Strong = Arc<T>; | ||
|
|
||
| fn upgrade(&self) -> Option<Self::Strong> { | ||
| self.upgrade() | ||
| } | ||
| } | ||
|
|
||
| impl<T> Downgrade for Rc<T> { | ||
| type Weak = rc::Weak<T>; | ||
|
|
||
| fn downgrade(&self) -> Self::Weak { | ||
| Rc::downgrade(self) | ||
| } | ||
| } | ||
|
|
||
| impl<T> Upgrade for rc::Weak<T> { | ||
| type Strong = Rc<T>; | ||
|
|
||
| fn upgrade(&self) -> Option<Self::Strong> { | ||
| self.upgrade() | ||
| } | ||
| } | ||
|
|
||
| #[doc(hidden)] | ||
| #[macro_export] | ||
| macro_rules! to_type_before { | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| (_) => (); | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| (self) => ( | ||
| compile_error!("Can't use `self` as variable name for the `clone!` macro. Try storing it in a temporary variable."); | ||
| ); | ||
| (@weak self) => ( | ||
| compile_error!("Can't use `self` as variable name for the `clone!` macro. Try storing it in a temporary variable."); | ||
| ); | ||
| ($variable:ident) => ( | ||
| let $variable = $variable.clone(); | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
| (@weak $variable:ident) => ( | ||
| let $variable = $crate::clone::Downgrade::downgrade(&$variable); | ||
| ); | ||
| } | ||
|
|
||
| #[doc(hidden)] | ||
| #[macro_export] | ||
| macro_rules! to_type_after { | ||
| (_ , $return_value:expr) => {}; | ||
| (self, $return_value:expr) => {}; | ||
| (@weak self, $return_value:expr) => {}; | ||
| ($variable:ident , $return_value:expr) => {}; | ||
| (@weak $variable:ident , $return_value:expr) => { | ||
| let $variable = match $crate::clone::Upgrade::upgrade(&$variable) { | ||
| Some(val) => val, | ||
| None => return ($return_value)(), | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
| }; | ||
| } | ||
|
|
||
| #[doc(hidden)] | ||
| #[macro_export] | ||
| macro_rules! to_return_value { | ||
| () => { | ||
| () | ||
| }; | ||
| ($value:expr) => { | ||
| $value | ||
| }; | ||
| } | ||
|
|
||
| /// Macro for passing variables as strong or weak references into a closure. | ||
| /// | ||
| /// This macro can be useful in combination with closures, e.g. signal handlers, to reduce the | ||
| /// boilerplate required for passing strong or weak references into the closure. It will | ||
| /// automatically create the new reference and pass it with the same name into the closure. | ||
| /// | ||
| /// If upgrading the weak reference to a strong reference inside the closure is failing, the | ||
| /// closure is immediately returning an optional default return value. If none is provided, `()` is | ||
| /// returned. | ||
| /// | ||
| /// ### Passing a strong reference | ||
| /// | ||
| /// ``` | ||
| /// use glib::clone; | ||
| /// use std::rc::Rc; | ||
| /// | ||
| /// let v = Rc::new(1); | ||
| /// let closure = clone!(v => move |x| { | ||
| /// println!("v: {}, x: {}", v, x); | ||
| /// }); | ||
| /// | ||
| /// closure(2); | ||
| /// ``` | ||
| /// | ||
| /// ### Passing a strong and weak reference | ||
| /// | ||
| /// ``` | ||
| /// use glib::clone; | ||
| /// use std::rc::Rc; | ||
| /// | ||
| /// let v = Rc::new(1); | ||
| /// let u = Rc::new(2); | ||
| /// let closure = clone!(v, @weak u => move |x| { | ||
| /// println!("v: {}, u: {}, x: {}", v, u, x); | ||
| /// }); | ||
| /// | ||
| /// closure(3); | ||
| /// ``` | ||
| /// | ||
| /// ### Providing a default return value if upgrading a weak reference fails | ||
| /// | ||
| /// ``` | ||
| /// use glib::clone; | ||
| /// use std::rc::Rc; | ||
| /// | ||
| /// let v = Rc::new(1); | ||
| /// let closure = clone!(@weak v => @default-return false, move |x| { | ||
| /// println!("v: {}, x: {}", v, x); | ||
| /// true | ||
| /// }); | ||
| /// | ||
| /// // Drop value so that the weak reference can't be upgraded. | ||
| /// drop(v); | ||
| /// | ||
| /// assert_eq!(closure(2), false); | ||
| /// ``` | ||
| #[macro_export] | ||
| macro_rules! clone { | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ($($(@ $weak:ident)? $variables:ident),+ => $(@default-return $return_value:expr,)? move || $body:block ) => ( | ||
| { | ||
| $( $crate::to_type_before!($(@ $weak)? $variables); )* | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| move || { | ||
| let return_value = || $crate::to_return_value!($($return_value)?); | ||
| $( $crate::to_type_after!($(@ $weak)? $variables, return_value );)* | ||
| $body | ||
| } | ||
| } | ||
| ); | ||
| ($($(@ $weak:ident)? $variables:ident),+ => $(@default-return $return_value:expr ,)? move | $($pattern:pat),* | $body:block ) => ( | ||
| { | ||
| $( $crate::to_type_before!($(@ $weak)? $variables); )* | ||
| move |$($pattern),*| { | ||
| let return_value = || $crate::to_return_value!($($return_value)?); | ||
| $( $crate::to_type_after!($(@ $weak)? $variables, return_value );)* | ||
| $body | ||
| } | ||
| } | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| extern crate glib; | ||
|
|
||
| use std::cell::RefCell; | ||
| use std::panic; | ||
| use std::rc::Rc; | ||
| use std::sync::{Arc, Mutex}; | ||
|
|
||
| use glib::clone; | ||
|
|
||
| struct State { | ||
| count: i32, | ||
| started: bool, | ||
| } | ||
|
|
||
| impl State { | ||
| fn new() -> Self { | ||
| Self { | ||
| count: 0, | ||
| started: false, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn clone_closure() { | ||
| let state = Rc::new(RefCell::new(State::new())); | ||
| assert_eq!(state.borrow().started, false); | ||
|
|
||
| let closure = { | ||
| clone!(@weak state => move || { | ||
sdroege marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| state.borrow_mut().started = true; | ||
| }) | ||
| }; | ||
|
|
||
| assert_eq!(closure(), ()); | ||
|
|
||
| assert_eq!(state.borrow().started, true); | ||
| assert_eq!(state.borrow().count, 0); | ||
|
|
||
| let closure = { | ||
| let state2 = Rc::new(RefCell::new(State::new())); | ||
| assert_eq!(state.borrow().started, true); | ||
|
|
||
| clone!(@weak state, state2 => move || { | ||
| state.borrow_mut().count += 1; | ||
| state.borrow_mut().started = true; | ||
| state2.borrow_mut().started = true; | ||
| }) | ||
| }; | ||
|
|
||
| assert_eq!(closure(), ()); | ||
|
|
||
| assert_eq!(state.borrow().count, 1); | ||
| assert_eq!(state.borrow().started, true); | ||
| } | ||
|
|
||
| #[test] | ||
| fn clone_default_value() { | ||
| let closure = { | ||
| let state = Rc::new(RefCell::new(State::new())); | ||
| clone!(@weak state => @default-return 42, move |_| { | ||
| state.borrow_mut().started = true; | ||
| 10 | ||
| }) | ||
| }; | ||
|
|
||
| assert_eq!(42, closure(50)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn clone_panic() { | ||
| let state = Arc::new(Mutex::new(State::new())); | ||
| state.lock().unwrap().count = 20; | ||
|
|
||
| let closure = { | ||
| let state2 = Arc::new(Mutex::new(State::new())); | ||
| clone!(@weak state2, state => @default-return panic!(), move |_| { | ||
| state.lock().unwrap().count = 21; | ||
| state2.lock().unwrap().started = true; | ||
| 10 | ||
| }) | ||
| }; | ||
|
|
||
| let result = panic::catch_unwind(|| { | ||
| closure(50); | ||
| }); | ||
|
|
||
| if result.is_ok() { | ||
| assert!(false, "should panic"); | ||
| } | ||
|
|
||
| assert_eq!(state.lock().unwrap().count, 20); | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.