Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Allow generic decoding of usage into a type #31

Merged
merged 1 commit into from
Oct 17, 2014

Conversation

alexcrichton
Copy link
Contributor

When writing a generic decode function over any types, one is tempted to write
something along the lines of:

extern crate serialize;
extern crate docopt;
use serialize::Decodable;

fn foo<'a, T>(usage: &str) -> T
              where T: Decodable<docopt::Decoder<'a>, docopt::Error> {
    let map = docopt::docopt(usage).unwrap_or_else(|e| e.exit());
    map.decode().unwrap_or_else(|e| e.exit())
}

Currently there are two problems with this approach.

  1. The docopt::Decoder structure is private, so it cannot be referenced in a
    function signature.
  2. The lifetime parameter on Decoder prevents usage of the local variable
    map. The decode function requires &'a self, which requires callers of
    this function to provide a lifetime variable 'a which is the lifetime of
    the stack frame of foo itself. Sadly this cannot currently be done (higher
    ranked trait lifetimes maybe?).

To work around these two problems, I've made the Decoder structure public and
changed the decode method on ValueMap to consume the map, removing the
lifetime parameter on Decoder.

There may be some more generic wizardry to get around this problem, but this at
least seems to work for now! Does this seem ok to you?

When writing a generic decode function over any types, one is tempted to write
something along the lines of:

    extern crate serialize;
    extern crate docopt;
    use serialize::Decodable;

    fn foo<'a, T>(usage: &str) -> T
                  where T: Decodable<docopt::Decoder<'a>, docopt::Error> {
        let map = docopt::docopt(usage).unwrap_or_else(|e| e.exit());
        map.decode().unwrap_or_else(|e| e.exit())
    }

Currently there are two problems with this approach.

1. The `docopt::Decoder` structure is private, so it cannot be referenced in a
   function signature.
2. The lifetime parameter on `Decoder` prevents usage of the local variable
   `map`. The `decode` function requires `&'a self`, which requires callers of
   this function to provide a lifetime variable `'a` which is the lifetime of
   the stack frame of `foo` itself. Sadly this cannot currently be done (higher
   ranked trait lifetimes maybe?).

To work around these two problems, I've made the `Decoder` structure public and
changed the `decode` method on `ValueMap` to consume the map, removing the
lifetime parameter on `Decoder`.

There may be some more generic wizardry to get around this problem, but this at
least seems to work for now!
BurntSushi added a commit that referenced this pull request Oct 17, 2014
Allow generic decoding of usage into a type
@BurntSushi BurntSushi merged commit 4544a9f into docopt:master Oct 17, 2014
@alexcrichton alexcrichton deleted the decode-consume branch October 17, 2014 22:27
@BurntSushi
Copy link
Member

Sounds good to me! Thank you!

(higher ranked trait lifetimes maybe?)

I think you mean higher kinded trait lifetimes? I've been wanting this for other reasons, like supporting streaming iterators. Here's some more context on the problem.

@alexcrichton
Copy link
Contributor Author

Ah yes, I believe that is what I mean! Perhaps rust-lang/rfcs#387 would help here (I'm not actually sure)

@BurntSushi
Copy link
Member

Yeah, that actually does look plausible. I eagerly await it because zero-cost safe streaming iterators are freaking cool. (I can't think of any other language that has them.)

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

Successfully merging this pull request may close these issues.

2 participants