Style question: associated fn or free fn for constructor?

I'm trying to decide which variant works better:

impl Foo {
  pub fn make(...) -> Self {
    Self { ... }
  }
}

or

pub fn make_foo(...) -> Foo {
  Foo { ... }
}

The advantage of having associated functions is that the documentation makes it easy to find (especially if a module has multiple structs, where all the free functions would be mixed together). But the downside is that you cannot import make directly, it has to be used as Foo::make. Whereas with make_foo, you could easily import and rename it as needed.

Are there other reasons to prefer the associated function (or avoid the free function)? If so, is there a workaround for being unable to import the function directly, or is it usually just not a big deal to spell out the full name of the function?

I prefer Foo::new, because that's what the standard library does almost everywhere and what people will be looking for when they want to create an instance of a type, first.

3 Likes

Implementing Foo::new is definitely more standard and will be easily found by anybody looking through the docs. Another good thing to do, if your new function doesn't take any parameters, is to implement Default and then have your new function just call your Default implementation:

impl Foo {
  pub fn new() -> Self {
    Self::default()
  }
}

impl Default for Foo {
  fn default() -> Self {
    Self { /* fields */ }
  }
}
1 Like

You don't normally see free functions used as constructors in Rust, that seems to be more of a C++ thing (e.g. std::make_unique()).

That said, if I need to do some non-trivial computation that returns a new instance of some type and it doesn't make sense to have it associated with the type, then it's perfectly fine to use a free function. I just wouldn't call it make_foo() (or new_foo(), because new is the naming convention for a constructor).

1 Like

I think for my API I need something more along the lines of a HashMap that maps function names to function pointers, but I was also forgetting that you can have associated functions in C so that might change something. I've never done FFI before, so I'm just getting into this for the first time, and the idea is still pretty fuzzy in my head.

I'll probably be coming on the forum with more topics on FFI pretty soon. :wink:

:face_with_raised_eyebrow:

I don't think there is such a notion in C... Are you maybe referring to this convention for implementing "methods"?

typedef struct Foo {
  int i;
} Foo;

Foo foo_new(int initial_i) {
  Foo f = { .i = initial_i };
  return f;
}

int foo_increment(Foo *f) {
  return ++f->i;
}

Please do, I'd be keen to see what you achieve!

1 Like

Oh, actually, I just got confused which topic this was in and totally misunderstood what you were saying as I thought it was in the context of a different post!

I'm just ignorant and thought you implied there was associated functions in C:

:sweat_smile:

1 Like

Thanks all! Seems like people don't mind the Foo:: prefix, and it's consistent with std and the wider ecosystem, so I'll stick constructors in impl blocks.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.