Why are type annotations needed for generics with defaults, yet Vec in nightly infers its allocator automatically?

Viewed 439

I am trying to define a struct with a defaultable type parameter, the same way Vec works in Rust nightly with its allocator. However, as explained in other answers and the Rust forum, that's not how type inference works:

use std::marker::PhantomData;

struct Foo<X: Copy = i32> {
    x: PhantomData<X>
}

impl<X: Copy> Foo<X> {
    fn new() -> Foo<X> {
        Foo {
            x: PhantomData
        }
    }
}

fn main() {
    // type annotations needed
    let foo = Foo::new();
    
    // works??
    let mut bar = Vec::new();
    bar.push(42); // to let Rust infer T.
}

My question is not why Foo::new() does not compile, but rather why Vec::new() still does. If I remove Foo::new(), the file compiles. As far as I understand it, the new definition of Vec to support allocators should've broken almost all code.

Ideally an answer would point me to the code in rustc that makes this exemption, if it is one.

1 Answers

The impl in which fn new is defined is generic over T, but not A:

impl<T> Vec<T> {
    pub const fn new() -> Self {
        // ...
    }
}

Since A has a default of Global, this implements a new function for Vec<T, Global>. It does not implement a new function for any other allocator. So Vec::new will always create a new Vec<_, Global>.

The fact that A has a default is actually tangential; that isn't taken into consideration when resolving Vec::new.

You can make Foo work the same way:

impl Foo<i32> {
    fn new() -> Self {
        Foo { x: PhantomData }
    }
}

Note that in this case new will always create a Foo<i32>, and if you want a function that creates a different kind of Foo, you'll need to create a differently-named function, just as Vec has new_in for specifying a different allocator than Global.

Related