how to specify that I want a generic type to support 'new'

Viewed 253

I want to have a factory method callable from C

#[no_mangle]
pub extern "C" fn make<T>() -> *mut T{
        Box::into_raw(Box::new(T::new()))
}

this doesn't work because rust doesn't know if T::new exists. I know I would make a trait if I wanted to know that a type supported a foo function that I defined , but new already exists.

2 Answers

Unlike C++, Rust does not have features like template-level implicit interface so it has to be done explicitly, like

fn main() {
    make(|| Test::new);
}

pub fn make<T>(cl: impl Fn() -> T) -> *mut T
where {
        Box::into_raw(Box::new(cl()))
}

struct Test;

impl Test{
    fn new() -> Test {
        Test
    }
}

playground


Besides that, the no_mangle attribute is not useful with generic functions. See the related github issue for more details.

new is not a so-called “trait” in Rust because the convention by design is open-ended; some new constructors take arguments, others don't, some might fail, others are infallible. new is purely a conventional name; it otherwise has no significant value in Rust. They are called constructors by convention, but it is a normal static method.

The trait you seem to be looking for is Default, which is defined for types whereof it makes sense to create a new value with no further arguments, creating a sensible default value, such as the number 0, or the boolean false, or an empty collection.

Alternatively, if you seek to convert from a single argument, the trait From is what you desire:

#[no_mangle]
pub extern "C" fn make<T: Default>() -> *mut T{
    Box::into_raw(Box::new(T::default()))
}

Or:

#[no_mangle]
pub extern "C" fn make_from<X, T: From<X>>(x: X) -> *mut T{
    Box::into_raw(Box::new(T::from(x)))
}

The Type: Trait constraint tells the type system that the type must satisfy a given trait.

If you not be familiar with traits in Rust yet, they are an essential part of Rust programming to master.

Related