Naming conventions for converters

pub struct FooBar { ... }

pub struct Dog { ... }

impl Dog {

  pub fn get_foobar(&self) -> FooBar { ... }
  pub fn get_FooBar(&self) -> FooBar { ... }
}

I think convention (snake case for function names) suggests get_foobar ; but I am increasingly thinking that get_FooBar makes more sense.

Anyone with strong opinions on this ? (Either direction is fine; but please keep it polite; and definitely do not accuse people of wanting to "fork the rust compiler" :slight_smile: ).

Rust style is pretty clear that it's get_foo_bar.

But you wouldn't use get either. That's usually for when the method name would otherwise not work and there's no meaningful name to use, like fn get(&self) or fn get_1(&self).

// If FooBar is a member of Dog
pub fn foo_bar(&self) -> &FooBar
// If FooBar can be constructed from Dog
pub fn to_foo_bar(&self) -> FooBar
// If FooBar is a representation of Dog (like AsRef)
pub fn as_foo_bar(&self) -> &FooBar
// If Dog can be converted to FooBar (like From/Into)
pub fn into_foo_bar(self) -> FooBar

There's some wiggle room here, but you wouldn't use get or non-snake-case.

You might also forgo this method completely and use AsRef<FooBar> or From<Dog> for FooBar. No case issues with those.

16 Likes

The only thing I have to say that @drewtato didn't already say better is...

When I see camel case functions[1] I think "hmm, newcomer to the language".[2] When I see a mix, I don't know what to think other than "inconsistent".


  1. or snake case structs, etc ↩︎

  2. Or rarely foreign language library interface (GTK or whatever) ↩︎

7 Likes

Every languages has its own convention to represents XMLHttpRequest.

1 Like

I'm not sure I agree with this. I think of Struct names as a single token. Adding "_" and changing it to lower case, when it appears in a function name, seems somewhat arbitrary.

FooBar is two words: a capital letter begins a new word in CamelCase. Converted to snake_case it is indeed foo_bar (that would be the name of the type as well if type names were snske_case as is common in eg. C). You might not agree but that’s the documented convention in Rust. For instance, AsRef has a method called as_ref, not asref.

(Numbers are an interesting edge case; would, say, Vec3d be vec_3d or vec3d snake_cased? I’d weakly prefer the latter, but it could go either way.)

1 Like

I think of it like: if adding _ to distinguish foo from bar doesn't make sense,why did it make sense to capitalize Bar in FooBar?

I get what you mean by a single token, but (from my POV) the inconsistency is further fallout from mixing styles.

Anyway, that's my opinion; not really interested in spending effort convincing others.

5 Likes

Naming conventions are to some extent arbitrary.

The return type already appears in the function signature, so there isn't a great need to repeat it verbatim in the name. Given that, something like generate_connection_graph just is more pleasant to read than generate_ConnectionGraph.

Another way of thinking of it: both struct FooBar and fn foo_bar() embody the concept of "foo bar". From this perspective, both are inheriting a name from the concept and not from each other.

I see the casing as the kind, while the words themselves are the idea.

3 Likes

I don't think we can speak of name formatting as making more or less sense. It's pretty arbitrary and the compiler does not care. We can however talk of consistency and conventions.

As such I have a number of objections to your suggestion:

  1. If we made the rules that:
    a) All type names are to be coloured pink
    b) All function names are to be coloured blue.

Then clearly your "get" function would be coloured blue. Making it half pink and half blue, the equivalent of your suggestion, would break rule b).

  1. "get_FooBar" suggests getting the type "FooBar". But you don't want the type definition, which would be "pub struct FooBar{...}", you want an actual instance of FooBar with some actual data in it not just a type.

All of which makes more "sense" to me.

  1. How ugly and confusing do you want to make your code? :slight_smile:
2 Likes

Yes, arbitrary. Could have been the other way around. Could have been something different. As I said, the compiler does not care.

But any other suggestion, including yours, is also arbitrary.

"FooBar" is a name of a type. Your function does not return the name of a type or the description of the type. It returns actual data of type FooBar. That is a different thing. So all is well with "get_foobar".

What if your get function could fail? Would you call your get function:

pub fn get_Result(&self) -> Result<FooBar> { ... }

or perhaps:

pub fn get_ResultFooBar(&self) -> Result<FooBar> { ... }

or

pub fn get_Result_FooBar(&self) -> Result<FooBar> { ... }

This gets out of hand...

1 Like

FooBar is a type name, you don't need to repeat a type name verbatim in a function name. if you insist the type name FooBar must appear at the call site, you can just (ab-)use type parameters.

e.g. instead of

let foobar = dog.get_FooBar();

you can make it

let foobar = dog.get::<FooBar>();
//or
let foobar = FooBar::get_from(&dog);

convention is always arbitrary, and what makes sense is always subjective, so you choose what you like. this is a non-problem to begin with. just remember to be consistent, otherwise future self will hate you.

3 Likes

The convention for this is to either not mention result at all (lots of operations are naturally fallible), or use a try_ prefix.

XMLHttpRequest is a classic bad name, as it doesn't actually care if it's XML, and these days most often it isn't.

The naming convention should be simple and easy to understand. I don't think there is any way to mix snakes and camels without making the rules complex and with lots of gray areas to argue over.

Recently, clippy told me that acronyms in type names should also be cameled, like Dna or Rna, which looks weird, but at least there is simple consistency.

I have a similar complaint about some of Rust std naming. BTreeSet and BTreeMap APIs don't expose anything related to B-trees in particular, they could be implemented as red-black trees. They should be called OrderedSet and OrderedMap.

BinaryHeap's API doesn't expose whether it's binary or a heap. It should be called PriorityQueue.

I'd even go as far as to say that Hash doesn't have anything to do with hashing (that's Hasher's job). Its job is to convert a data structure into a stream of bytes, so it should be called Serialize or something like that (and indeed, notice how serde's Serialize trait is similar to Hash, and Serializer to Hasher).

And similarly HashMap should be called simply Map (or UnorderedMap).

The name XMLHttpRequest is worse in that it suggests an API property that isn't true: one might think it requires the content to be XML, but in fact you can use it for other content, too.

With regards to std collections, I imagine there are performance characteristics, too. I've been assuming that hash-based collections are more optimized for larger collections. Also, maybe in the future, we might add more collections.

In my mind, Hash and Serialize are quite different. Hash should be something that can be quickly turned into a number that is always the same, while Serialize means it can be turned into something that represents its entire information.

I agree it's worse.

Well yes -- BTreeMap (aka "ordered map") has to maintain an ordering, whereas HashMap (aka "unordered map") doesn't, so the latter can be faster. It's already implied by the "ordered" adjective.

Hash implementations don't compute the hash. Hash converts anything into a stream of bytes (more-less) that is supposed to represent its relevant data. Then a Hasher implementation takes that stream of bytes and turns it into a number according to some hashing algorithm. So Hasher is the right name, but Hash is basically a serialization trait, it doesn't involve any hashing.