3

I always thought pass by value is a legacy from the early languages, because the designers had never seen anything else. But after seeing the brand new languages like Go adapting the same principle confused me.

The only advantage I can think of is you make sure that the value you are passing won't be modified. But every time you need to pass a big structure to a function, you need to pass the pointer (or reference) to prevent copying, if the language permits it.

To prevent modification of parameters, some constness mechanism could be introduced instead of making the whole language pass by value.

I have been coding for years and I rarely needed to pass by value. Almost always, I don't need my value/object to be copied to a function call; and often I don't need it to be modified inside the function*.

What is the motivation behind making modern languages pass by value? Are there any advantages that I am not aware of?

Edit: When I say pass by value, I don't mean primitives. They are easy and cheap to pass by value. My main focus is objects or structs that consists of more than 1 primitive and thus expensive to copy.

*: I have asked around and others reported similar thing with their parameter usage.

nimcap
  • 623
  • 1
  • 5
  • 12
  • 1
    Passing by reference for a complex, multiple layered object can lead to pointer confusion. Some languages (.NET definitely, Java maybe) do not perform deep copies of objects. The result is hard to find bugs when you think you are working with a new instance of a child object when it is really a shared child object of every object that was copied/cloned. I too rarely pass by value. – Adam Zuckerman Feb 22 '14 at 20:38
  • 2
    @AdamZuckerman: if you are passing by ref when you don't need to (and I am includinging performance reasons when dealing with large structures as "need to") you are doing the next programmer to work on your code a disservice, and that is assuming you understand what you are doing. It is non-standard behaviour which can introduce subtle and hard to track down bugs. It makes all of your variables globals. – jmoreno Feb 22 '14 at 22:05
  • I didn't say that I don't do it. In places where I want to modify the passed in variable without affecting the caller, of course I would pass by value. My comment was about some languages not eating their own dog food. – Adam Zuckerman Feb 22 '14 at 22:37
  • @AdamZuckerman: does "passed in variable" mean passed in object, the variable in the calling function or the local parameter? Not knowing the difference between these is a common misunderstanding that leads to people using by ref when they shouldn't. By Ref is used to change the variable in the calling function, something that is a fairly uncommon necessity even in languages without support for returning multiple values. – jmoreno Feb 23 '14 at 02:02
  • If you declare a variable of any type in a routine, then use that variable as an argument to another routine, passing that variable by value prevents the called routine from modifying the variable in the calling routine. Passing it by ref allows the called routine to modify the value of the variable in the calling routine. I hope this is a clearer statement of what I tried to say before. Some languages do not have the concept of multiple return values. Some languages do not have the concept of any return values. In the latter case, everything would be passed by ref or global in scope. – Adam Zuckerman Feb 23 '14 at 03:59
  • That’s a pretty horrible answer as far as I’m concerned, and the most upvoted answer in that thread actually answers your question. – Konrad Rudolph Feb 23 '14 at 20:49

3 Answers3

14

Pass-by-reference makes some things more efficient and allows updates to parameters within subroutines, but it is by no means a panacea.

Imagine a parallelized application. When using pass-by-reference, then you need a locking mechanism to keep a sane state. Pass-by-value doesn't have this limitation. I've seen multi-threaded programs which spend more time waiting for mutexes than processing data. Here pass-by-value can be a big efficiency gain.

Imagine a distributed application. This is always pass-by-value, because the remote system needs its own copy of the object. Pass-by-reference must be simulated using proxy objects. It requires extra network overhead to keep the proxy objects in sync. Here pass-by-value save not only processing time, but reduces network bandwidth.

Jeffery Thomas
  • 2,115
  • 1
  • 14
  • 19
7

Pass by value is just a corollary to value semantics (i.e. by-value variables, object members, etc). Aside from the advantage you mention, this adds:

  • Very efficient aggregates. A type like struct Foo { T a; U b; } does not require a separate allocation or extra indirection for the members.
  • Zero cost abstractions: class IntWithDifferentBehavior { int value; ... } is exactly as efficient as an int, assuming static dispatch for the methods.
  • It goes very well with tracking ownership and lifetimes. One consequence is support for deterministic destruction, enabling RAII as an alternative to (semi-)manual resource management.

In addition, pass-by-value is much easier to make memory safe and efficient. If you pass pointers/references to stack-allocated memory, you need to make sure the memory outlives the reference. If you instead use automatic memory management to ensure that, you pay a run-time price for that.

  • I take it your code samples are in C++, right? Because they would be also valid in C#, and there `class` is not a zero-cost abstraction. – svick Feb 22 '14 at 22:10
  • 2
    @svick They're not in any language in particular. And no, in C# it is not a zero cost abstraction, because classes in C# don't have value semantics. That is *exactly* what my answer is saying. –  Feb 23 '14 at 08:44
  • 1
    Passing values by reference is much easier to make memory-safe and efficient than passing references by value. Passing pointers is unsafe, but in frameworks like .NET which support pass-by-reference semantics, any "byref" passed to a method is guaranteed to be destroyed when it goes out of scope. – supercat Feb 25 '14 at 00:33
  • @supercat *How* does it guarantee that? I'm pretty certain it requires further work that isn't necessary when passing the same thing without `ref`. Perhaps language-level restrictions on what can be done with `ref` parameters? Those need to be defined and checked, mind you. –  Feb 25 '14 at 11:37
  • @delnan: In .NET, the code verifier will not allow a byref to be copied to any kind of storage location of non-automatic duration, nor used as a return value of any user-defined function [array indexing returns a byref, but that's a special case]. While it would be possible to define slightly more permissive rules and still uphold the required guarantees, the rule as it exists achieves 99% of the benefits of byrefs without overly complicating verification. – supercat Feb 25 '14 at 13:45
  • @supercat Interesting. That's indeed not very complicated, but it's still strictly more work than for non-`ref` things, so I stand by my claim that it's. Note that rules of this sort fall under the "make sure the memory outlives the reference" umbrella; I did not intend to imply those checks need to performed manually. Rust is another example of a language that does such memory safety checks automatically, though it is much more permissive and complicated than the CLR rules. –  Feb 25 '14 at 13:58
  • @delnan: Work needs to be done someplace. If it is e.g. necessary for a thread-safe method to return two values to a caller and a framework doesn't support byrefs, then it will be necessary to either have the caller pass a temporary heap object, have the called method create a temporary heap object and return a reference, or store the data in a thread-local object. Except in the case where the caller can keep recycling a single object for many function calls, any of those approaches will require more work than passing a byref. To be sure, there are other ways such work could be avoided... – supercat Feb 25 '14 at 14:13
  • ...if a framework had functions leave their arguments on the stack when they return, and a language allowed parameters to be declared as copy-in-copy-out. On the other hand, for better or for worse, support for byrefs makes it possible for a framework to support atomic methods upon primitives and references without them having to be embedded within special atomic types [I say "for worse" because that could potentially prevent implementation of a framework on a system which required atomic variables to be in a dedicated address space]. – supercat Feb 25 '14 at 14:17
  • @supercat What, exactly, are you trying to say? I'm not sure what point you are trying to make, and not sure either whether it's relevant to this question or this answer. Regarding your example, I either don't fully understand it, or the problem is trivial to solve by returning an aggregate/record by value. In the CLR, this would be a `struct`, other languages have anonymous records or tuples with the same properties. –  Feb 25 '14 at 14:24
  • @delnan: If a language has aggregate types, they can be used for that purpose, but not all languages have such types (Java in particular has neither byrefs nor aggregates, and thus requires the workarounds I described). In any case, byref support is not particularly expensive and has some unique advantages (like the ability to perform atomic operations directly upon primitives and references). – supercat Feb 25 '14 at 14:42
2

I always thought pass by value is a legacy from the early languages, because the designers had never seen anything else.

This is a bogus characterization in two respects:

  1. It (kind of) assumes that pass-by-value was the original form of passing, and that it was superseded by better things. In fact, the history clearly demonstrates this is not true (as does the Answer you cited as a source).

  2. It implies (without any logical argument or supporting evidence) that the designers of mainstream programming languages are ignorant of programming language history. A proper review of the relevant documentation will reveal that this notion is completely false ... not to mention insulting to the people involved.


The actual decision to support pass-by-reference, pass-by-value and/or one of the more obscure / historic forms (such as pass-by-copying or pass-by-name) is heavily intertwined with other aspects of the overall language design ... and motivated by the goals of the design.

For example:

  • Is the goal to provide a language where programs are easy to read and understand?

  • Is the goal to provide a language which maximizes "expressive power" or "conciseness"?

  • Is the goal to provide a language which maximizes "performance" on certain kinds of problems?

  • Is the goal to support a particular programming "paradigm" over others; e.g. procedural, OO, functional, pure functional, parallel / concurrent?

And so on.

In short, they have gone way beyond simplistic thinking like "the advantages of pass by value".


I have been coding for years and I rarely needed to pass by value. Almost always, I don't need my value/object to be copied to a function call.

Something does not make sense with that ...

If you have been coding for years in Java, C, C++, C# or most other modern programming languages, you have been using pass by value most of the time. It is the default way of doing things ... and in some languages (like Java) it is the only way.

For example

    int i = 1;
    myFunction(i);

AFAIK, i will be passed by value in C, C++, Java and C#. If you want pass by reference (in C and C++) you have to write it explicitly; e.g.

    myOtherFunction(&i);

Do you actually do that all the time in your code? For primitive-typed variables? I don't think so.

Stephen C
  • 25,180
  • 6
  • 64
  • 87
  • Of course I am aware that language designers are not ignorant and it is very hard to design languages. I am sorry if I offended someone. Actually, first I wrote something like 'When I say value I don't mean primitives of course.' but I thought it is obvious, I decided to omit it for clarity in my original post. But now I will update my question accordingly. – nimcap Feb 23 '14 at 02:55
  • @nimcap: C# and Java use pass by value for complex objects, not just for primitives. Mind you, they pass a reference by value, but this is not at all the same thing as passing a value by reference. C# also supports structs (in which case the behavior is similar to pass by value in C++) and pass by reference (in which case the behavior is similar to pass by reference in C++). – Brian Feb 23 '14 at 16:18
  • Actually I might be misusing the terms. I used the term _pass-by-value_ when the actual object passed to a function is copied, not the reference. – nimcap Feb 23 '14 at 17:28
  • 3
    @nimcap - Yes. If that is what you meant, then you are misusing the term. Pass-by-value is actually talking about any kind of value. – Stephen C Feb 24 '14 at 08:15