Generic constructor for generic type in Rust

404 Name Not Found

TL;DR: How do I guarantee I can construct arbitrary structs with WhateverStruct::new()?

This:

trait Constructable {
    fn new() -> Self;
}

struct Foo {
    bar: u8
    /* whatever else it may contain */
}

impl Constructable for Foo {
    fn new(baz: u8) -> Self {
        return Self { bar: baz };
    }
}

doesn't work, since new() is now not conforming to Constructable.


Back story:

I am trying to learn Rust by making a linked list.

In C++, Typescript, and many other languages, I can write my linked list without having to worry whether a constructor for the data type my linked list is going to store exists or not:

template<typename T>
class LinkedListNode<T> {
    public:
        LinkedListNode(T data) { // This class doesn't care where T comes from or who constructed it or how it was constructed
            this->data = data;
        }

        T data;
};

template<typename T>
class LinkedList<T> {
    public:
        LinkedList() {
            this->head = nullptr;
        }

        /* whatever a linked list does */

        std::shared_ptr<LinkedListNode<T>> head;
};

In Rust, structs don't seem to have the concept of a constructor by default, and so "constructor"s (WhateverStruct::new()) are not guaranteed to exist.

Therefore, I made myself a guarantee it will exist:

trait Constructible {
    fn new() -> Self;
}

So now I can do:

struct LinkedListNode<'a, T> { // a lot of convincing had to be done regarding lifetimes
    data: T,
    next: Option<&'a LinkedListNode<'a, T>> // similar to how I needed a pointer in C++ so the struct isn't infinitely recursive
}

struct LinkedList<'a, T> { // a lot of convincinng had to be done here as well
    head: Option<LinkedListNode<'a, T>>
}

So far so good. So now I want to test if my attempt at ensuring a "constructor" exists worked by making a new type to store in my linked list:

struct Foo {
    bar: u8
    /* whatever else it may contain */
}

impl<T> Constructable for Foo {
    fn new() -> Self {
        //
    }
}

I don't know what Foo might contain, but let's ignore that for now and suppose I do. I still have a problem:

impl Constructible for Foo {
    fn new(baz: u8) -> Self { // <- expected 0 params, got 1 param
        return Foo { bar: baz };
    }
}

Now the new function is no longer conforming to the Constructable trait.

What can I do to guarantee I can construct something?


A commenter wrote:

I'm not seeing any relationship between Constructible and the linked list, though.

I suppose I neglected to include why I thought I needed Constructible because I thought the post was long already, but here it is:

impl<'a, T> Constructable for LinkedListNode<'a, T> where T: Constructable {
    fn new(/* whatever args for T here */) -> Self {
        return Self { T::new(/* whatever args for T here */), None };
    }
}

fn main() {
    let l: LinkedList::<Foo> = LinkedList::new();
    l.push(/* whatever it takes to build Foo */);
}
Chayim Friedman

The correct way is, just like with C++, to take a parameter of type T in the constructor of LinkedListNode (be it an actual constructor or the push() method of the list). Also, the correct way is to not learn Rust by making a linked list - this is a bad choice; and if you feel you must, please read Learn Rust With Entirely Too Many Linked Lists.

But about the actual question you asked, this is possible, although not as nicely as with C++ and TypeScript with their variadic parameters. Make the constructor accepts a single, generic argument. This argument can be () (the unit type) to specify no additional arguments are required; it can be of any type; or it can be of tuple type, to specify multiple arguments.

trait Constructable<Arg> {
    fn new(arg: Arg) -> Self;
}

struct Foo {
    bar: u8
    /* whatever else it may contain */
}

impl Constructable<u8> for Foo {
    fn new(baz: u8) -> Self {
        return Self { bar: baz };
    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related