Convention for functions which return values which are Copy?

We have lots of conventions around method naming, such as:

  • If the method takes a self reference and returns a reference, call it as_xxx
  • If the method takes self by value and returns a value, call it into_xxx

Is there a convention around methods which take a self reference, but return a value because that value implements Copy? into_xxx doesn't seem right because it doesn't consume self. as_xxx doesn't seem right because it doesn't return a reference.

I believe that the name for that is just xxx:

struct Foo {
    data: usize
}

impl Foo {
    pub fn data(&self) -> usize {
         self.data
    }
}
1 Like

I'm under the impression that I was once told to use to_xxx in that case. I did so, but my code isn't widely used, so there's no chance for feedback on that choice in my case :slight_smile:

At the very least, that's true for non-Copy types. E.g., &str has to_string, which returns a String. But String isn't Copy.

That sounds reasonable to me.

EDIT: Looks like a lot of methods on Copy primitives like usize are named using to_xxx, but that seems to be only when there are explicit conversions involved (rather than just accessing existing data) like usize::to_le.

Does it matter though for the naming if the return type is Copy? I do remember that my function returned a Vec, so you're right, but I'm kinda wondering why you'd want to make that distinction.

I do agree with @OptimisticPeach's suggestions if you're returning a field of a struct this way, but it does sound somewhat wrong for other use cases.

I'm thinking that if the type isn't Copy, then you want to make clear that you're having to construct a new value in order to return it.

My particular use case is a family of container types that, in the normal case, hold types which are Copy. So it works for me. But I think it may be less obvious if you're not just exposing encapsulated values.

I use to_* for the reference → value case, but I don't bother to distinguish between Copy and non-Copy. For me, the important aspect is the combination of "does this consume the thing I have" and "do I own the thing I get back", which leads to as_*, to_* and into_*.

But, those prefixes are only for functions that are logically doing some kind of conversion of the subject value as a whole. In general, I suffix functions that return references, and don't prefix/suffix anything else.

1 Like

While i agree that your intention may further help you or the user of your function. I think the function signature is enough to tell what it does to say the least.

That's true, but function signatures don't help when reading existing code, which is the more common and arguably more important use case.

You mean one have to check the implementation of the following

pub fn swap<T>(x: &mut T, y: &mut T)

or

pub const fn size_of<T>() -> usize

before s/he know what it does? I think name and signature should suffice.

No, I mean that if you're reading somebody else's code or code that you wrote a while ago, it's much easier to read if you can tell what the functions do from their names rather than having to look up their signatures.

2 Likes

Albeit not sacrificing the overall readability of the function:

pub fn usize_data_get_mut_ref(&mut self) -> &mut usize

But something like so:

pub fn mut_data(&mut self) -> &mut usize

Would work nicely.