Idiomatic constructor name when returning a smart pointer

Sorry for the long post for a really quite simple question…

What is a good Rustic name for a constructor that returns a new object by shared pointer (e.g. Box, or in my particular case Arc, as I need multiple threads to have read-only access to an acyclic graph)? What if that’s a pointer to a trait object, because the object is only ever used via the trait?

I’ve looked at the API guidelines, which advise that constructors be called new(), with_xyz() or from_xyz(). new() seems most appropriate, but Clippy doesn’t like new() returning a type that doesn’t include Self or the exact type somewhere, which makes me wary.

For those who prefer code, here’s a trivial example in C++:

class Thing {
    public:
        virtual ~Thing() noexcept = default;

        // All I *ever* want to do with things is to treat
        // them with the contempt that they deserve.
        virtual void treat_with_contempt() = 0;
};

class TreadOnLego final:
    public Thing
{
    public:
        void treat_with_contempt() override { ... }

        // Named contructor.
        static auto new_thing(...)
            -> std::shared_pointer<Thing>
        {
            return std::make_shared<TreadOnLego>(...);
        }
};

And a Rust translation:

trait Thing {
    fn treat_with_comtempt(&self);
}

struct TreadOnLego;

impl Thing for TreadOnLego { ... }

impl TreadOnLego {
    // What should I call this?
    #[must_use]
    pub fn new_thing(...) -> Arc<dyn Thing> { ... }
}

So far I’ve considered things like new_traitname(), new_boxed() (but I’m using Arc, so new_arced()??), new_shared(), but nothing looks familiar with any other Rust code I’ve seen.

(EDIT: I am open to being told that this is a ridiculous antipattern if it is, that’s one reason I’m asking!)

Assuming for the moment that you can't leave allocation to the caller and, for some reason, must control it in this constructor function, then I would be strongly tempted to silence Clippy, leave a short explanatory comment, and call it new.

2 Likes

That was my default preference too, but the resulting code had its meaning obfuscated by too many calls to Arc::new(), and explicit typing of variables to : Arc<dyn Thing> because type inference was inferring Arc<TreadOnLego>, causing errors later.

I think new_shared or arc would be good options - the latter mirrors Box::pin which returns a Pin<Box<T>>.

2 Likes