Unable to infer enough type information about `_`, but i defined

Hi, nice to me you. I am a new man using rust to write web project. I try to archive a JWT, but i meet a probleam when i write a Token trait.

My mind is write a common trait for every string which is Tokenable. So i can use tokenString.decode() to get a decode token that include header and claims.

But the error occur in signature method, i have no idea for solving this error. Are there someone guide me to solve this? Thank you very much.

pub trait Tokenable<B> {
    type DecodeToken;

    fn decode(&self, secret: &[u8], algorithm: Algorithm) -> Result<Self::DecodeToken, Error>;
    fn signature(&self, secret: &[u8], algorithm: Algorithm) -> String;
}


impl<B> Tokenable<B> for String where B: Base64able{
    type DecodeToken = DeToken<B>;

    fn decode(&self, secret: &[u8], algorithm: Algorithm) -> Result<Self::DecodeToken, Error> {
        let token_data: Vec<&str> = self.rsplit('.').collect();
        if token_data.len() != 3 {
            return Err(Error::InvalidToken);
        }

        let header: Header = try!(Header::from_base64(token_data[3]));
        let claims: B = try!(B::from_base64(token_data[2]));
        let sign = token_data[1];

        // Validate algorithm
        if header.alg != algorithm {
            return Err(Error::InvalidAlgorithm);
        }

        // Validate signature
        let payload: String = [token_data[3], token_data[2]].join(".");
        let re_sign: String = payload.signature(secret, header.alg);
        if re_sign != sign {
            return Err(Error::InvalidSignature);
        }

        let de_token = DeToken::new(header, claims);
        Ok(de_token)
    }

    fn signature(&self, secret: &[u8], algorithm: Algorithm) -> String {
        fn crypt<D: Digest>(digest: D, payload: &str, secret: &[u8]) -> String {
            let mut hmac = Hmac::new(digest, secret);
            hmac.input(payload.as_bytes());
            hmac.result().code().to_base64(base64::URL_SAFE)
        }

        match algorithm {
            Algorithm::HS256 => crypt(Sha256::new(), self, secret),
            Algorithm::HS384 => crypt(Sha384::new(), self, secret),
            Algorithm::HS512 => crypt(Sha512::new(), self, secret),
        }
    }}

This is error showing: :frowning:

src/lib.rs:132:39: 132:68 error: unable to infer enough type information about _; type annotations or generic parameter binding required [E0282]
src/lib.rs:132 let re_sign: String = payload.signature(secret, header.alg);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:132:39: 132:68 help: run rustc --explain E0282 to see a detailed explanation
error: aborting due to previous error
Could not compile jsonwebtoken.

You need to surround your code in fences

```rust
like this
```

The problem is that you're trying to call the signature method on a String and not Tokenable. payload is obviously of type String in your code:

let payload: String = [token_data[3], token_data[2]].join(".");
let re_sign: String = payload.signature(secret, header.alg);

It must be some type that implements Tokenable to have the decode method.

Thank u for your good advice

Hi, thanks, but i impl tokenable for String :grin:

I couldn't see that in the code block you posted previously without formatting.

The compiler doesn't have enough information to infer the B parameter for the Tokenable<B> trait. You can specify the parameter like this:

let re_sign = Tokenable::<Foo>::signature(&payload, secret, header.alg);

where Foo is some type that implements the Base64able trait.

Specifically, I think you want to pass the B parameter in that case, as in:

let re_sign = <String as Tokenable<B>>::signature(&payload, secret, header.alg);

Hi, thanks. :slight_smile:

In my design, i impl tokenable to string, the payload also is string type, so i call payload.signature()

to sign directly, so the method need two param , secret and header.alg.

In this case, how can i solve the issuse?

hi , thanks very much, this my mind

Method calls are actually just sugar for longer functions like the ones we posted. So this:

payload.signature(secret, header.alg)

means the same thing as:

<String as Tokenable<_>>::signature(&payload, secret, header.alg)

Note that &payload is in the position of the &self parameter in the definition of the method.

The problem is that the compiler can't figure out that you want to treat it as a tokenable of the same type B as the impl block, so you need to specify that. It doesn't seem matter here what type B is, but the compiler also doesn't know that it doesn't matter. An easy way to tell the compiler what type to use is to use the longer form of the function.

Hi, Mr withoutboats. I change my tokenable trait. before my tokenable trait impl for String only,

so if i has a str like "abcdefg", i cant use decode() method in this str. So i want to tokenable impl for

str, string and all like string type.

like below.

pub trait Tokenable<B> {
    fn decode(&self, secret: &[u8], algorithm: Algorithm) -> Result<DeToken<B>, Error>;
    fn signature(&self, secret: &[u8], algorithm: Algorithm) -> String;
}


impl<B, T> Tokenable<B> for T where B: Base64able, T: AsRef<str> {
    fn decode(&self, secret: &[u8], algorithm: Algorithm) -> Result<DeToken<B>, Error> {
        let token_data: Vec<&str> = self.rsplit('.').collect();
        if token_data.len() != 3 {
            return Err(Error::InvalidToken);
        }

        let header: Header = try!(Header::from_base64(token_data[3]));
        let claims: B = try!(B::from_base64(token_data[2]));
        let sign = token_data[1];

        // Validate algorithm
        if header.alg != algorithm {
            return Err(Error::InvalidAlgorithm);
        }

        // Validate signature
        let payload: String = [token_data[3], token_data[2]].join(".");
        let re_sign: String = <String as Tokenable<B>>::signature(&payload, secret, header.alg);
        if re_sign != sign {
            return Err(Error::InvalidSignature);
        }

        let de_token = DeToken::new(header, claims);
        Ok(de_token)
    }

    fn signature(&self, secret: &[u8], algorithm: Algorithm) -> String {
        fn crypt<D: Digest>(digest: D, payload: &str, secret: &[u8]) -> String {
            let mut hmac = Hmac::new(digest, secret);
            hmac.input(payload.as_bytes());
            hmac.result().code().to_base64(base64::URL_SAFE)
        }

        match algorithm {
            Algorithm::HS256 => crypt(Sha256::new(), self, secret),
            Algorithm::HS384 => crypt(Sha384::new(), self, secret),
            Algorithm::HS512 => crypt(Sha512::new(), self, secret),
        }
    }
}

but i meet a error:

Compiling jsonwebtoken v1.0.0 (file:///Users/xiongbingchao/Developer/rust-jwt)
src/main.rs:107:42: 107:53 error: no method named rsplit found for type &T in the current scope
src/main.rs:107 let token_data: Vec<&str> = self.rsplit('.').collect();
^~~~~~~~~~~
src/main.rs:107:42: 107:53 help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item rsplit, perhaps you need to implement it:
src/main.rs:107:42: 107:53 help: candidate #1: core::str::StrExt

You have to call as_ref() every time you want to use a str method on T because that's what converts T to a str.