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

Can ScopeGuards be returned? #13

Closed
radix opened this issue Sep 2, 2017 · 6 comments
Closed

Can ScopeGuards be returned? #13

radix opened this issue Sep 2, 2017 · 6 comments

Comments

@radix
Copy link

radix commented Sep 2, 2017

Hi, I'm not really sure if ScopeGuard does what I'm looking for (or if it's possible at all in Rust).

I can't find any examples of code that returns ScopeGuards without parameterizing the guard closure type.

Imagine I have a collection and I would like to implement a get_mut which returns a mutable reference to an element of the collection. Also imagine that I need to perform some bookkeeping with the element after it's been mutated by user-code. I'd like to return a ScopeGuard wrapped around the element with my own closure that will do that bookkeeping. I haven't been able to figure out how to do this since I can't figure out how to write the ScopeGuard type (but this may just be my lack of experience with Rust).

Here's some example code which wraps a HashMap. I'm not really sure what the heck I'm doing with that second type parameter for ScopeGuard; I got there just by following compiler errors.

  pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> ScopeGuard<Option<&mut V>, for<'r> FnMut(&'r mut Option<&mut V>) -> (), scopeguard::Always>
  where
    <V as DeriveKey>::KeyType: ::std::borrow::Borrow<Q>,
    Q: hash::Hash + Eq,
    { guard(self.data.get_mut(k), |v| ()) }

but this isn't working because rustc is saying that the for<'r> FnMut type is not Sized.

So I'd just like to see if what I'm trying to do is even possible before further banging my head against it :)

@burdges
Copy link

burdges commented Sep 2, 2017

A bare FnMut is a trait object, which cannot be Sized. And ditto your HRTB type.

I think a boxed trait object Box<FnMut(&mut Option<&mut V>)> sounds like overkill for a function that does nothing, but rust-lang/rfcs#1522 should cover it nicely :

pub fn get_mut<Q>(&mut self, k: &Q) -> ScopeGuard<Option<&mut V>, impl FnMut(&mut Option<&mut V>), scopeguard::Always>
    where Q: ?Sized+hash::Hash + Eq,
        <V as DeriveKey>::KeyType: ::std::borrow::Borrow<Q>,
    { guard(self.data.get_mut(k), |v| ()) }

@radix
Copy link
Author

radix commented Sep 3, 2017

Ok, so I messed around with impl Trait a lot until I figured out the incantation I need:

  pub fn get_mut<'a, Q: ?Sized>(&'a mut self, k: &Q) -> ScopeGuard<Option<&'a mut V>, impl for<'r> FnMut(&'r mut Option<&'a mut V>) -> (), scopeguard::Always>
  where
    <V as DeriveKey>::KeyType: ::std::borrow::Borrow<Q>,
    Q: hash::Hash + Eq,
    
    {
      guard(self.data.get_mut(k), |v| ())
    }

Here's a complete self-contained example:

pub struct Container(u8);
fn guard_test<'a>(container: &'a mut Container) -> ScopeGuard<&'a mut u8, impl for<'r> FnMut(&'r mut &'a mut u8) -> (), scopeguard::Always> {
  guard(&mut container.0, |v| ())
}

@radix
Copy link
Author

radix commented Sep 3, 2017

even more complete example:

#[derive(Debug)]
pub struct Container(u8);

fn guard_test<'a>(
  container: &'a mut Container
) -> ScopeGuard<&'a mut u8, impl for<'r> FnMut(&'r mut &'a mut u8) -> (), scopeguard::Always> {
  guard(&mut container.0, |v| { **v = **v + 1; })
}


  #[test]
  fn guards() {
    let mut c = Container(3);
    {
      let mut value = guard_test(&mut c);
      **value = **value + 1;
    }
    assert_eq!(c.0, 5);
  }

@bluss
Copy link
Owner

bluss commented Sep 3, 2017

Hi, great that you have resolved the syntactical issues.

I think that this is not what the scopeguard crate is implementing or has as use case, it's implementing a guard for a lexical scope, and it is not designed to be returned or to “guard” something else.

What you would typically do is to use a custom type that keeps the book keeping for you. Look closely at how std's RefCell::borrow and Mutex::lock work.

@bluss bluss closed this as completed Sep 3, 2017
@radix
Copy link
Author

radix commented Sep 5, 2017

Hi @bluss! I'm not sure why scopeguard is inappropriate for this use case -- it seems to do exactly what I want. Is there a hidden infelicity in my example? I realize I could create a custom type with Drop, but scopeguard makes it much easier to set this up.

@bluss
Copy link
Owner

bluss commented Sep 5, 2017

There's a lot about your problem that I don't know. If it is unsafe critical that the cleanup code runs, then scope guard is not a solution, since if you give the user the scope guard, they can just choose to forget it to skip its drop. That the return type can't be properly expressed in stable Rust is another good reason I would not use it like this.

autarch added a commit to houseabsolute/pg-pretty that referenced this issue Mar 14, 2021
…text stack

The format_range_var_table_sample method was pushing a context without
popping. Having a macro that ensures both are always done simplifies this. I
originally wanted this to be a method that returned a guard, but I'm not sure
how to specify the ScopeGuard<...> type parameters in the return type. See
bluss/scopeguard#13 for discussion of this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants