<p>Returning a value from a function like <code>int add(int,int);</code> allows for a subset of the functionality of simply passing a pointer <code>void add(int,int,*int);</code>.</p>
<p>We see a mixture of pointers and return values used for results in many C APIs which introduces additional complexity.</p>
<p>In a minimal language (like C) why would it bother including an extra keyword for <code>return</code> statements? I'm interested in anyones
insight here.</p>
<p>I know why a language would allow for a <code>break</code> or <code>return;</code> without a value to allow early exiting, but returning a value is the aspect I'm curious about people's thoughts.</p>
<hr />
<p>To give some context, I'm deciding on whether to include <code>return</code> statements in my own language.</p>

<functions>
Return statements vs passing a pointer
CC BY-SA 4.0
2023-06-27T15:52:33.897Z
I think this kind of misses the point of out parameters in C. Often, you use them for things like strings, so that the caller gets to decide how the memory is allocated. For example, `char buffer[10];` vs a heap-allocated buffer. This isn't possible with just return values. Also, there are lots of other examples of there being a "less general" version of a feature in lots of languages: `for` loops (just a special case of `while` loops), `switch` statements (just a special case of multiple `if`s, etc.
CoffeeTableEspresso
CC BY-SA 4.0
2023-06-27T15:53:25.347Z
If you don't care about memory allocation, you can always just return a structure in C with whatever contents you want (the same way some other languages would let you return multiple values or a tuple).
CoffeeTableEspresso
CC BY-SA 4.0
2023-06-27T19:50:32.213Z
Congratulations on being the first question to reach HNQ!
Seggan
<p>Clarity. Returns are easier to grok then out parameters.</p>
<p>I might be making some enemies here... But out parameters smell. And they smell worse then old fish. There is nothing inherently <em>wrong</em> with an out parameter... But there are a couple of downsides to using them.</p>
<p>Consider:</p>
<pre><code>int x = foo();
</code></pre>
<p>what does this function do? no idea, but I know that it is returning an <code>int</code> which is assigned to <code>x</code>. That much is really clear. And with better function/result names I can learn everything I need to about the function by just looking at the function call. No need to inspect it.</p>
<p>what about if we have a function that uses an out parameter such as:</p>
<pre><code>int x;
foo(&amp;x);
</code></pre>
<p>What is happening here? no idea. What I know is that foo takes a pointer to x, I don't really know what it does with that pointer... maybe it assigns it a new value.. maybe x is supposed to have a value and foo takes an int pointer as an input so there is an error here... I cannot say without looking at foo's documentation.</p>
<p>Now increase the complexity, and create more complicated function calls. That require multiple inputs, have multiple outputs etc...</p>
<p>&quot;But out parameters allow for multiple returned variables&quot;... SRP (single responsibility principle), A function should do 1 thing. Multiple outputs are a sign that a function is doing too many things. If it really needs to return multiple values because they are related, why aren't you keeping those variables together as a <code>struct</code> to make certain that nothing is lost or mixed?</p>
<p>The subset of functionality offered by <code>returns</code> vs <code>out parameters</code> is a good thing, because it encourages better coding practices, makes code more readable, etc...</p>

CC BY-SA 4.0
2023-06-27T18:02:15.187Z
2023-06-27T18:19:26.937Z
2023-06-27T18:19:26.937Z
Questor
CC BY-SA 4.0
2023-06-27T18:07:31.070Z
To an extent, this can be mitigated by having different operators for write-only vs read-write pointers, e.g. C# `out` vs `ref`.
Bbrk24
CC BY-SA 4.0
2023-06-27T21:38:30.327Z
@Bbrk24 Yes, notably C has a keyword for read-only: `const`. But no keyword for write-only.
Stef
<p>It is easier to read the code where arguments and results are possible to tell apart without viewing the signature of the function:</p>
<pre><code>(r1, r2) = f(a1, a2)
</code></pre>
<p>is more easy to understand than something like <code>f(a1, a2, r1, r2)</code>.</p>
<p>As seen in the example, multiple return values are also possible with tuples that can be immediately unpacked into separate variables (r1 and r2).</p>
<p>If r1 and r2 are more tightly coupled (like X and Y co-ordinates of the location, for instance), it is usually advocated that the function should return the properly defined structure instead, that would name the values for clarity. Tuples are more recommended when the values are not very related, like location and error code, for instance.</p>

CC BY-SA 4.0
2023-06-28T14:35:26.993Z
2023-06-28T15:26:50.633Z
2023-06-28T15:26:50.633Z
Audrius Meškauskas
CC BY-SA 4.0
2023-12-12T19:53:22.780Z
On the visibility front, in C# arguments are labeled with `out` at the call site, for `out` parameters.
Pablo H
<p>Various languages have, over the years, developed many different ways to handle returning values from functions -- often simultaneously.  They tend to serve different needs.</p>
<p>A single return value is convenient because it allows easier composition of expressions that would otherwise have to be captured into variables and later operated on separately.</p>
<p>Output/reference parameters are convenient where a value is produced as a side effect (especially where not always needed).  There's an interesting side argument here whether it's better to pass a null pointer in when a particular side output is not required -- this allows the callee to skip computation of the value, which might be a performance benefit; but also requires conditional logic, which might be a performance loss.</p>
<p>Reference parameters are convenient where you want your function to sometimes leave a value unchanged, or when you want the caller to control the memory allocation.</p>
<p>Modern language design seems more inclined to gravitate towards returning multiple values for the cases where a single function has multiple logical returns, typically combined with a destructuring assignment or similar to then decompose these into individual variables for later use.</p>
<p>Others argue that multiple return values are a code smell and should be rewritten to separate function calls, or introduce a new class that encapsulates the combined result (e.g. did you really want to return a <code>bool,value</code> pair or is that better expressed as an <code>optional&lt;T&gt;</code> or a <code>ResultOrError&lt;T&gt;</code> etc?).  Typically this sort of thing only works well with generic/template types or it can lead to an explosion of tiny types, especially for one-off use cases.</p>
<p>Ultimately it's going to come down to a question of how strongly opinionated do you want your language to be, and whether it's a low-level language where people want to finely control memory allocation or a higher-level language where such things are more abstracted.</p>

CC BY-SA 4.0
2023-06-28T01:10:22.460Z
2023-06-28T01:10:22.460Z
Miral
<h1>Register optimization</h1>
<p>Simply said, any value that's small enough to fit in one (or even a couple of) register will be quicker to return via one (usually RAX in AMD64 systems) than if the caller had to allocate space for the return value and the callee access said memory space.</p>

CC BY-SA 4.0
2023-06-26T14:09:16.933Z
2023-06-26T14:09:16.933Z
kouta-kun
CC BY-SA 4.0
2023-06-26T14:12:02.440Z
Would a tool like LLVM be able to perform this register optimisation with `void add(int,int,*int);`? I would expect it could?
Jonathan Woollett-light
CC BY-SA 4.0
2023-06-26T14:18:14.230Z
@JonathanWoollett-light That seems unlikely, optimizers are rarely able to cross function boundaries especially with pointers, so unless the function is inlined it probably won't fix it by default. Even when inlined it may not remove the pointer, as that's quite a rare situation and hard to optimize without changing behavior.
mousetail 'he-him'
CC BY-SA 4.0
2023-06-26T14:32:52.490Z
@JonathanWoollett-light in a non-inlined case I wouldn't expect it since you could be passing it a volatile pointer, a memory address to an existing struct or something along those lines. Inlining does optimize it though, at least in this contrived godbolt example: https://godbolt.org/z/KMvca16Mf
kouta-kun
CC BY-SA 4.0
2023-06-28T00:45:38.663Z
An optimiser is not allowed to change how parameters are passed to functions, but a calling convention is -- for example the FASTCALL convention has rules about passing a certain number of register-sized parameters in registers instead of on the stack.  Importantly, both sides have to agree to this in advance, typically by including a common header file or other interface specification that explicitly indicates that this convention should be used instead of the default.  A new language/platform design could declare a register-based convention is the default, provided you have a fallback.
Miral
<p>When a function call returns a value, that means a function call is an expression which can be used within other expressions where a value is expected. Consider an expression like <code>f() + g()</code> for example. If <code>f</code> and <code>g</code> had <a href="https://en.wikipedia.org/wiki/Parameter_(computer_programming)#Output_parameters" rel="noreferrer">out-parameters</a> instead of return values, then this could not be written as an expression; you would need to write something like</p>
<pre class="lang-c prettyprint-override"><code>int f_out, g_out;
f(&amp;f_out);
g(&amp;g_out);
f_out + g_out
</code></pre>
<p>This is impractical, and so even when languages support out-parameters, generally you can <em>also</em> return values, and out-parameters are only used for the cases where returning a single value from a function isn't sufficient.</p>
<hr />
<p>So it's not really a question of choosing between out-parameters (or pointers) vs. return values; you should generally have return values, and then decide if you want to <em>also</em> support out-parameters. The main argument against out-parameters is that they transmit function results as a <a href="https://en.wikipedia.org/wiki/Side_effect_(computer_science)" rel="noreferrer">side-effects</a> ─ calling the function mutates the variable passed in, changing the program state.</p>
<p>One of the most consistent things in the evolution of programming language design over the last several decades has been to prevent side-effects, particularly mutation, either by forbidding them entirely or by enabling the programmer to avoid them where they are unwanted. It's widely considered that immutability and function purity are beneficial even in the imperative paradigm, since mutation is the source of whole categories of bugs.</p>

CC BY-SA 4.0
2023-06-26T14:34:20.653Z
2023-06-26T15:33:02.463Z
2023-06-26T15:33:02.463Z
kaya3
CC BY-SA 4.0
2023-06-26T14:36:24.093Z
"last several decades has been to prevent side-effects" dishonorable mention to C# which added out parameters in the middle of the functional programming explosion
kouta-kun
CC BY-SA 4.0
2023-06-27T15:08:41.257Z
@kouta-kun `ref` and `out` are entirely different things in C#. Most out-params (not ref-params) I've seen have been used as an equivalent to Swift/Rust `if let`, i.e. `if (Guid.TryParse(str, out var guid))`.
Bbrk24
# Return statements vs passing a pointer
Posted by: username_0 on 2023-06-26
Returning a value from a function like `int add(int,int);` allows for a subset of the functionality of simply passing a pointer `void add(int,int,*int);`.

We see a mixture of pointers and return values used for results in many C APIs which introduces additional complexity.

In a minimal language (like C) why would it bother including an extra keyword for `return` statements? I'm interested in anyones
insight here.

I know why a language would allow for a `break` or `return;` without a value to allow early exiting, but returning a value is the aspect I'm curious about people's thoughts.

---

To give some context, I'm deciding on whether to include `return` statements in my own language.
**Tags:** functions
> **Comments:**
> **username_1**: I think this kind of misses the point of out parameters in C. Often, you use them for things like strings, so that the caller gets to decide how the memory is allocated. For example, `char buffer[10];` vs a heap-allocated buffer. This isn't possible with just return values. Also, there are lots of other examples of there being a "less general" version of a feature in lots of languages: `for` loops (just a special case of `while` loops), `switch` statements (just a special case of multiple `if`s, etc.  
> **username_1**: If you don't care about memory allocation, you can always just return a structure in C with whatever contents you want (the same way some other languages would let you return multiple values or a tuple).  
> **username_2**: Congratulations on being the first question to reach HNQ!  
---
## 5 answers
### Answer by username_9 on 2023-06-26. Score: 13 | Accepted answer
# Register optimization

Simply said, any value that's small enough to fit in one (or even a couple of) register will be quicker to return via one (usually RAX in AMD64 systems) than if the caller had to allocate space for the return value and the callee access said memory space.
> **Comments:**
> **username_0** (2023-06-26): Would a tool like LLVM be able to perform this register optimisation with `void add(int,int,*int);`? I would expect it could?  
> **username_10** (2023-06-26): @JonathanWoollett-light That seems unlikely, optimizers are rarely able to cross function boundaries especially with pointers, so unless the function is inlined it probably won't fix it by default. Even when inlined it may not remove the pointer, as that's quite a rare situation and hard to optimize without changing behavior.  
> **username_9** (2023-06-26): @JonathanWoollett-light in a non-inlined case I wouldn't expect it since you could be passing it a volatile pointer, a memory address to an existing struct or something along those lines. Inlining does optimize it though, at least in this contrived godbolt example: https://godbolt.org/z/KMvca16Mf  
> **username_8** (2023-06-28): An optimiser is not allowed to change how parameters are passed to functions, but a calling convention is -- for example the FASTCALL convention has rules about passing a certain number of register-sized parameters in registers instead of on the stack.  Importantly, both sides have to agree to this in advance, typically by including a common header file or other interface specification that explicitly indicates that this convention should be used instead of the default.  A new language/platform design could declare a register-based convention is the default, provided you have a fallback.  
---
### Answer by username_11 on 2023-06-26. Score: 18
When a function call returns a value, that means a function call is an expression which can be used within other expressions where a value is expected. Consider an expression like f() + g() for example. If f and g had out-parameters instead of return values, then this could not be written as an expression; you would need to write something like
int f_out, g_out;
f(&f_out);
g(&g_out);
f_out + g_out

This is impractical, and so even when languages support out-parameters, generally you can also return values, and out-parameters are only used for the cases where returning a single value from a function isn't sufficient.

So it's not really a question of choosing between out-parameters (or pointers) vs. return values; you should generally have return values, and then decide if you want to also support out-parameters. The main argument against out-parameters is that they transmit function results as a side-effects ─ calling the function mutates the variable passed in, changing the program state.
One of the most consistent things in the evolution of programming language design over the last several decades has been to prevent side-effects, particularly mutation, either by forbidding them entirely or by enabling the programmer to avoid them where they are unwanted. It's widely considered that immutability and function purity are beneficial even in the imperative paradigm, since mutation is the source of whole categories of bugs.
> **Comments:**
> **username_9** (2023-06-26): "last several decades has been to prevent side-effects" dishonorable mention to C# which added out parameters in the middle of the functional programming explosion  
> **username_4** (2023-06-27): @kouta-kun `ref` and `out` are entirely different things in C#. Most out-params (not ref-params) I've seen have been used as an equivalent to Swift/Rust `if let`, i.e. `if (Guid.TryParse(str, out var guid))`.  
---
### Answer by username_3 on 2023-06-27. Score: 6
Clarity. Returns are easier to grok then out parameters.
I might be making some enemies here... But out parameters smell. And they smell worse then old fish. There is nothing inherently wrong with an out parameter... But there are a couple of downsides to using them.
Consider:
int x = foo();

what does this function do? no idea, but I know that it is returning an int which is assigned to x. That much is really clear. And with better function/result names I can learn everything I need to about the function by just looking at the function call. No need to inspect it.
what about if we have a function that uses an out parameter such as:
int x;
foo(&x);

What is happening here? no idea. What I know is that foo takes a pointer to x, I don't really know what it does with that pointer... maybe it assigns it a new value.. maybe x is supposed to have a value and foo takes an int pointer as an input so there is an error here... I cannot say without looking at foo's documentation.
Now increase the complexity, and create more complicated function calls. That require multiple inputs, have multiple outputs etc...
"But out parameters allow for multiple returned variables"... SRP (single responsibility principle), A function should do 1 thing. Multiple outputs are a sign that a function is doing too many things. If it really needs to return multiple values because they are related, why aren't you keeping those variables together as a struct to make certain that nothing is lost or mixed?
The subset of functionality offered by returns vs out parameters is a good thing, because it encourages better coding practices, makes code more readable, etc...
> **Comments:**
> **username_4** (2023-06-27): To an extent, this can be mitigated by having different operators for write-only vs read-write pointers, e.g. C# `out` vs `ref`.  
> **username_5** (2023-06-27): @Bbrk24 Yes, notably C has a keyword for read-only: `const`. But no keyword for write-only.  
---
### Answer by username_8 on 2023-06-28. Score: 3
Various languages have, over the years, developed many different ways to handle returning values from functions -- often simultaneously. They tend to serve different needs.

A single return value is convenient because it allows easier composition of expressions that would otherwise have to be captured into variables and later operated on separately.

Output/reference parameters are convenient where a value is produced as a side effect (especially where not always needed). There's an interesting side argument here whether it's better to pass a null pointer in when a particular side output is not required -- this allows the callee to skip computation of the value, which might be a performance benefit; but also requires conditional logic, which might be a performance loss.

Reference parameters are convenient where you want your function to sometimes leave a value unchanged, or when you want the caller to control the memory allocation.

Modern language design seems more inclined to gravitate towards returning multiple values for the cases where a single function has multiple logical returns, typically combined with a destructuring assignment or similar to then decompose these into individual variables for later use.

Others argue that multiple return values are a code smell and should be rewritten to separate function calls, or introduce a new class that encapsulates the combined result (e.g. did you really want to return a `bool,value` pair or is that better expressed as an `optional` or a `ResultOrError` etc?). Typically this sort of thing only works well with generic/template types or it can lead to an explosion of tiny types, especially for one-off use cases.

Ultimately it's going to come down to a question of how strongly opinionated do you want your language to be, and whether it's a low-level language where people want to finely control memory allocation or a higher-level language where such things are more abstracted.
---
### Answer by username_6 on 2023-06-28. Score: 1
It is easier to read the code where arguments and results are possible to tell apart without viewing the signature of the function:
(r1, r2) = f(a1, a2)

is more easy to understand than something like f(a1, a2, r1, r2).
As seen in the example, multiple return values are also possible with tuples that can be immediately unpacked into separate variables (r1 and r2).
If r1 and r2 are more tightly coupled (like X and Y co-ordinates of the location, for instance), it is usually advocated that the function should return the properly defined structure instead, that would name the values for clarity. Tuples are more recommended when the values are not very related, like location and error code, for instance.
> **Comments:**
> **username_7** (2023-12-12): On the visibility front, in C# arguments are labeled with `out` at the call site, for `out` parameters.  
---
