ChatGPT解决这个技术问题 Extra ChatGPT

Is it good style to explicitly return in Ruby?

Coming from a Python background, where there is always a "right way to do it" (a "Pythonic" way) when it comes to style, I'm wondering if the same exists for Ruby. I've been using my own style guidelines but I'm thinking about releasing my source code, and I'd like it to adhere to any unwritten rules that might exist.

Is it "The Ruby Way" to explicitly type out return in methods? I've seen it done with and without, but is there a right way to do it? Is there maybe a right time to do it? For example:

def some_func(arg1, arg2, etc)
  # Do some stuff...
  return value # <-- Is the 'return' needed here?
end
This is a very old question, but for those that have similar questions, The book Eloquent Ruby is highly recommended. It isn't a style guide as much as an introduction to writing idiomatic Ruby. I wish more people would read it!
As someone who inherited a large legacy app that was developed in Ruby, I very much appreciate that you asked this question. This deserves an upvote. This is littered throughout our legacy app. One of my jobs on this team is to improve the codebase (hard when being new to Ruby).

E
EliadL

Old (and "answered") question, but I'll toss in my two cents as an answer.

TL;DR - You don't have to, but it can make your code a lot more clear in some cases.

Though not using an explicit return may be "the Ruby way", it's confusing to programmers working with unfamiliar code, or unfamiliar with this feature of Ruby.

It's a somewhat contrived example, but imagine having a little function like this, which adds one to the number passed, and assigns it to an instance variable.

def plus_one_to_y(x)
    @y = x + 1
end

Was this meant to be a function that returned a value, or not? It's really hard to say what the developer meant, as it both assigns the instance variable, AND returns the value assigned as well.

Suppose much later, another programmer (perhaps not that familiar with how Ruby does returns based on last line of code executed) comes along and wants to put in some print statements for logging, and the function becomes this...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
end

Now the function is broken if anything expects a returned value. If nothing expects a returned value, it's fine. Clearly if somewhere further down the code chain, something calling this is expecting a returned value, it's going to fail as it's not getting back what it expects.

The real question now is this: did anything really expect a returned value? Did this break something or not? Will it break something in the future? Who knows! Only a full code review of all calls will let you know.

So for me at least, the best practice approach is to either be very explicit that you are returning something if it matters, or return nothing at all when it doesn't.

So in the case of our little demo function, assuming we wanted it to return a value, it would be written like this...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    return @y
end

And it would be very clear to any programmer that it does return a value, and much harder for them to break it without realizing it.

Alternatively, one could write it like this and leave out the return statement...

def plus_one_to_y(x)
    @y = x + 1
    puts "In plus_one_to_y"
    @y
end

But why bother leaving out the word return? Why not just put it in there and make it 100% clear what's happening? It will literally have no impact on your code's ability to perform.


Interesting point. I think I actually ran into a similar situation when I asked this question, where the original developer used the implicit return value in their code, and it was very difficult to understand what was going on. Thanks for your answer!
I found the question while Googling on implicit returns, as I had just been burned by this. I had added some logic to the end of a function that was implicitly returning something. Most calls to it didn't care (or check) the returned value, but one did - and it failed. Fortunately at least it showed up in some functional tests.
While true, it's important for code to be readable, eschewing a well-known feature of a programming language in favor of verbosity is, in my opinion, bad practice. If a developer isn't familiar with Ruby, then perhaps they should get familiar before touching the code. I find it a bit silly to have to increase the clutter of my code just for the future event that some non-Ruby developer has to do something later. We shouldn't be reducing a language to the lowest common denominator. Part of the beauty of Ruby is that we don't have to use 5 lines of code to say something that should only take 3.
If the word return adds semantic meaning to the code, why not? It's hardly very verbose - we're not talking 5 lines vs 3, just one more word. I'd prefer to see return at least in functions (maybe not in blocks) so as to express what the code actually does. Sometimes you have to return mid-function - don't leave out the last return keyword on the last line just because you can. I don't favor verbosity at all, but I favor consistency.
This is a great answer. But it still leaves the risk that you write a Ruby method intending to be a procedure (aka function returning unit) e.g. side_effect() and another developer decides to (ab)use the implicit return value of your procedure. To fix this, you can make your procedures explicitly return nil.
M
Miguel

No. Good Ruby style would generally only use an explicit returns for an early return. Ruby is big on code minimalism/implicit magic.

That said, if an explicit return would make things clearer, or easier to read, it won't harm anything.


What is the point of code minimalism if it results in confusion maximalism?
@JonCrowell It should instead result in documentation maximalism.
@jazzpi If you have to heavily document your code for it to be well understood you're not practicing minimalism, you're practicing code golf.
It's not confusing to exclude the final return, it's confusing to INCLUDE it - as a Ruby developer, return already has a significant semantic meaning as an EARLY EXIT to the function.
@DevonParsons How on earth could someone confuse a return on the last line as an EARLY EXIT anyway?
J
Jörg W Mittag

I personally use the return keyword to distinguish between what I call functional methods, i.e. methods that are executed primarily for their return value, and procedural methods that are executed primarily for their side-effects. So, methods in which the return value is important, get an extra return keyword to draw attention to the return value.

I use the same distinction when calling methods: functional methods get parentheses, procedural methods don't.

And last but not least, I also use that distinction with blocks: functional blocks get curly braces, procedural blocks (i.e. blocks that "do" something) get do/end.

However, I try not to be religious about it: with blocks, curly braces and do/end have different precedence, and rather than adding explicit parentheses to disambiguate an expression, I just switch to the other style. The same goes for method calling: if adding parentheses around the parameter list makes the code more readable, I do it, even if the method in question is procedural in nature.


Right now I omit a return in any "one-liners", which is similar to what you're doing, and I do everything else the same. Good to know I'm not completely off in my style. :)
@Jorg: Do you use early returns in your code? Does that cause any confusion with your procedural/functional scheme?
@Andrew Grimm: Yes, and Yes. I'm thinking of inverting it. The vast majority of my methods is referentially transparent / pure / functional / whatever you want to call it anyway, so it makes sense to use the shorter form there. And methods written in functional style tend to not have early returns, since that implies the notion of "step", which is not very functional. Although, I don't like returns in the middle. I either use them in the beginning as guards or at the end to simplify control flow.
Great answer, but it's actually better practice to call procedural methods with () and functional methods without - see my answer below for why
A
Alex Dean

Actually the important thing is to distinguish between:

Functions - methods executed for their return value Procedures - methods executed for their side effects

Ruby does not have a native way of distinguishing these - which leaves you vulnerable to writing a procedure side_effect() and another developer deciding to abuse the implicit return value of your procedure (basically treating it as an impure function).

To resolve this, take a leaf out of Scala and Haskell's book and have your procedures explicitly return nil (aka Unit or () in other languages).

If you follow this, then using explicit return syntax or not just becomes a matter of personal style.

To further distinguish between functions and procedures:

Copy Jörg W Mittag's nice idea of writing functional blocks with curly braces, and procedural blocks with do/end When you invoke procedures, use (), whereas when you invoke functions, don't

Note that Jörg W Mittag actually advocated the other way around - avoiding ()s for procedures - but that's not advisable because you want side effecting method invocations to be clearly distinguishable from variables, particularly when arity is 0. See the Scala style guide on method invocation for details.


If I need the returned value from function_a, and function_a was written to call (and can only operate by calling) procedure_b, then is function_a truly a function? It returns a value, fulfilling your requirement for functions, but it has a side effect, fulfilling the requirements of procedures.
You are right Devon - function_a could contain a tree of procedure_... calls, as indeed procedure_a could contain a tree of function_... calls. Like Scala but unlike Haskell, there can be no guarantee in Ruby that a function is not side-effecting.
n
ndnenkov

The style guide states, that you shouldn't be using return on your last statement. You can still use it if it's not the last one. This is one of the conventions which the community follows strictly, and so should you if you plan to collaborate with anyone using Ruby.

That being said, the main argument for using explicit returns is that it's confusing for people coming from other languages.

Firstly, this is not entirely exclusive to Ruby. For example Perl has implicit returns too.

Secondly, the majority of the people that this applies to are coming from Algol languages. Most of those are way "lower level" than Ruby, hence you have to write more code to get something done.

A common heuristic for method length (excluding getters/setters) in java is one screen. In that case, you might not be seeing the method definition and/or have already forgotten about where you were returning from.

On the other hand, in Ruby it is best to stick to methods less than 10 lines long. Given that, one would wonder why he has to write ~10% more statements, when they are clearly implied.

Since Ruby doesn't have void methods and everything is that much more concise, you are just adding overhead for none of the benefits if you go with explicit returns.


"This is one of the conventions which the community follows strictly" My experience is that no, people don't follow this strictly unless they are very experienced Ruby developers.
@TimHolt I must have been very lucky then. (:
@ndn completely agree. The important thing is that when a method (or class) starts to exceed 5/100 lines, then it's a good idea to reconsider what you're trying to accomplish. Really Sandi's Rules are a cognitive framework for thinking about code as opposed to an arbitrary dogma. The majority of code I encounter isn't problematic because it's artificially constrained -- it's generally the opposite -- classes with multiple responsibilities, methods that are longer than many classes. Essentially too many people "jump the shark" -- mixing responsibilities.
Sometimes convention is still a bad idea. Its a stylistic convention that relies on magic, makes return types difficult to guess at. offers no advantage other than saving 7 keystrokes and is incongruent with almost all other languages. If Ruby ever does a Python 3 style consolidation and simplification pass , I would hope implicit anything is the first on the chopping block.
Except clearly it does not actually make things easier to read! Magic and comprehensibility are not the ame thing.
D
Devon Parsons

I agree with Ben Hughes and disagree with Tim Holt, because the question mentions the definitive way Python does it and asks if Ruby has a similar standard.

It does.

This is such a well-known feature of the language that anyone expected to debug a problem in ruby should reasonably be expected to know about it.


I agree with you in theory, but in practice, programmers make mistakes and mess up. Case in point, we all know that you use "==" in a conditional and not "=", but we ALL have made that mistake before and didn't realize it until later.
@TimHolt I'm not addressing any particular use case, I'm just answering the question. It is explicitly bad style, as chosen by the community, to use the return keyword when unnecessary.
Fair enough. I'm saying that the style you mention can lead to mistakes if you aren't careful. Based on my experience, I personally advocate a different style.
@TimHolt By the way, this question was asked way before the style guide was written, so the answers that disagree aren't necessarily wrong, just outdated. I stumbled here somehow though and didn't see the link I provided, so I figured someone else might end up here too.
It is not an official "standard". The ruby style guide used by rubocop and linked in above with the claim there there is by Devon Parsons, is created by a non-core ruby hacker. So the comment by Devon is factually simply wrong. Of course you may STILL use it but it is not a standard.
P
Praveen Angyan

It's a matter of which style you prefer for the most part. You do have to use the keyword return if you want to return from somewhere in the middle of a method.


G
Gishu

Like Ben said. The fact that 'the return value of a ruby method is the return value of the last statement in the function body' causes the return keyword to be rarely used in most ruby methods.

def some_func_which_returns_a_list( x, y, z)
  return nil if failed_some_early_check


  # function code 

  @list     # returns the list
end

you could also write "return nil if failed_some_early_check" if your intention is still clear
@Gishu I was actually complaining about the if statement, not the method name.
@Andrew.. oh the missing end :). Got it. fixed to pacify the other comment too from Mike.
Why not if failed_check then nil else @list end? That is truly functional.
@cdunn2001 Not clear on why if then else is functional.. the reason for this style is that it reduces nesting via early exits. IMHO also more readable.