I found a nice and visual metaphor for this. It won't work for aphantasiacs (probably) as it requires imagination of surreal objects.
First is to make clear distinction between values and addresses. But I won't call those "values" and "addresses". I'll call those boxes and ... tentacles!
Yes! Tentancles! So imagine a number as a box, and imagine this box extrude or generate tentacles. Many tentacles, or no tentacles. Some tentacles are named, other anonymous. But all these tentacles belong to (originate from) same box.
So, a function is just a transformer box -- you put stuff in, and get some result out. But due to technical limitation of this transformer box, all the stuff we put in -- we put just a copy of that stuff. Yeah, each time we run a function machine, we make copies of boxes and use those there. But this also allows us to preserve original boxes, so that's good news.
But functions are fine with putting tentacles as inputs. Yes, we have to duplicate that tentacle, but it still originates from some box. And when function acts on tentacle, it first "pulls" the box and does some work. It can potentially modify that box. It was tentacle what was made a copy, not a box itself.
So pass-by-value is when function expects a copy of box, and pass-by-ref is when function expects a copy of box tentacle.
(this metaphor is quite generalizable. Tentacle has a small box on it's end. And that small box can also generate tentacles! int**
essentially)
Most other answers are good, but I found that abstract "address" or "index" are hard to imagine, so they are ignored and this starts doomed spiral of failure to understand. Thinking of "address" or "index" already requires rule-based thinking, which we can't assume in general audience.
foo(x)
can not assign the variablex
ifx
is passed by value. It maybe can mutate the object to whichx
refers, but it can not change which objectx
refers to. $\endgroup$