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

Allow generic decoding of usage into a type#31

Merged
BurntSushi merged 1 commit intodocopt:masterfrom
alexcrichton:decode-consume
Oct 17, 2014
Merged

Allow generic decoding of usage into a type#31
BurntSushi merged 1 commit intodocopt:masterfrom
alexcrichton:decode-consume

Conversation

@alexcrichton
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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