Unable to infer enough type information


#1

Today I was trying to implement (Minisat’s) Lit (Literal) type that is a 32-bit sized data structure that merges a 31-bit value (representing a VarRef) and a 1-bit sign-like type for space optimization.

For this I have wrote a tuple-struct that has consists of a single private u32 type member.

Access to the intern value is given by the following methods:

struct VarRef(pub u32);
enum Sign{ Pos, Neg }
struct Lit(u32);

impl Lit {
    pub fn new<V: Into<VarRef>, S: Into<Sign>>(var: V, sign: S) -> Self
        where V: Into<VarRef>, S: Into<Sign> {..}

    pub fn var(&self) -> VarRef {..}
    pub fn sign(&self) -> Sign {..}
    pub fn negate(&self) -> Self {..}
}

The internal bit-structure was to put the sign-bit as the most-valueable-bit.
The var, sign and negate methods were not a problem at all.
However, I had type inference problems in the new method that tries to merge the given
parameters var and sign into the single u32.

My first try was the obvious:

    pub fn new<V: Into<VarRef>, S: Into<Sign>>(var: V, sign: S) -> Self
        where V: Into<VarRef>,
              S: Into<Sign>
    {
        it(var.into().into() + (sign.into().into() << 31))
    }

There I use the implemented From trait to cast the given val into a VarRef
which itself is a tuple-struct of a single u32 and cast that to a u32.
The sign parameter was treated similarly but with the conversions to Sign and u32
with a left-bit-shift of 31 bits.

On compiling the compiler stated the following:
types/lit.rs:21:18: 21:22 error: unable to infer enough type information about_; type annotations or generic parameter binding required [E0282] types/lit.rs:21 Lit(var.into().into() + (sign.into().into() << 31)) ^~~~

The solution to this was:

    pub fn new<V: Into<VarRef>, S: Into<Sign>>(var: V, sign: S) -> Self
        where V: Into<VarRef>,
              S: Into<Sign>
    {
        let v: VarRef = var.into();
        let s: Sign   = sign.into();
        let vi: u32 = v.into();
        let si: u32 = s.into();
        Lit(vi + si << 31)
    }

Which is equivalent to what I am doing above with just the added type information that the compiler was asking for.
However, I find this kind of frustrating that the compiler isn’t able to infer this information.

Questions

  • What are the reasons for that?
  • Am I doing something wrong in the current implementation?
  • Is my usage of the From trait too elaborated?

Regards


#2

The following works:

        Lit(<_ as Into<u32>>::into(var.into()) + (<_ as Into<u32>>::into(sign.into()) << 31))

Not that I would suggest writing it like that, but it should give an idea of what’s going on.

The problem actually has to do with the way the + operator is implemented, using std::ops::Add. The compiler sees that the Lit constructor expects a u32, so it can deduce the output of the addition is u32… but that doesn’t give it any direct information about the inputs (there are multiple implementations of Add that return a u32). On the flip side, there are multiple implementations of Into for VarRef (Into<u32>, Into<VarRef>, Into<Box<VarRef>>, etc.). This is where things break down: there isn’t any reason to prefer a particular version of Into, so deduction has nowhere to go.


#3

Thank you very much for your reply.
I think I understood the problem why the compiler can’t infere it and agree that the “solution” code you have shown isn’t favorable.