Infer if type through indirection.

25ce6d4
Opened by Kevin Cox at 2022-03-21 12:08:49

It surprised me that the first example works, while the second doesn't.

https://play.rust-lang.org/?gist=a1bee92067f87a67240b4ccd2bc9a6a4&version=stable

trait Foo {}

struct A;
impl Foo for A {}

struct B;
impl Foo for B {}

pub fn test() {
    let a = A;
    let b = B;

    // Works.
    let x: &Foo = if true { &a } else { &b };

    // Doesn't work.
    let y = if true { &a } else { &b };
    let z: &Foo = y;
}
error[E0308]: if and else have incompatible types
  --> src/main.rs:17:13
   |
17 |     let y = if true { &a } else { &b };
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `A`, found struct `B`
   |
   = note: expected type `&A`
              found type `&B`

It seems like adding this let shouldn't change anything but it seems the required type of the if statement isn't propagated unless it is directly used. The same behaviour can be found when using func(if ...) vs let x = if ...; func(x)

(sorry if this is known, I couldn't find anything)

  1. Should the last line be

    let z: &Foo = y;
    

    (It still doesn't work with that change, but right now the z line isn't doing anything.)

    scottmcm at 2017-11-21 22:38:37

  2. Yes, good catch. Of course what I posted doesn't work because y is never used. However with that change in would expect it to work (but it doesn't).

    I've updated the original comment to be correct.

    Kevin Cox at 2017-11-21 22:42:33

  3. Triage: still reproduces on 2021 edition, tested on rustc 1.59.0 (9d1b2106e 2022-02-23)

    Code needs be adjusted a bit using the dyn keyword:

    trait Foo {}
    
    struct A;
    impl Foo for A {}
    
    struct B;
    impl Foo for B {}
    
    pub fn main() {
        let a = A;
        let b = B;
    
        // Works.
        let x: &dyn Foo = if true { &a } else { &b };
    
        // Doesn't work.
        let y = if true { &a } else { &b };
        let z: &dyn Foo = y;
    }
    

    Maayan Hanin at 2022-03-21 12:08:49