Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I agree about your greater point of learning about history being important, but very much disagree about your specific gripe. What Ironman says is:

  7.2.F. (7F.)  FORMAL PARAMETER CLASSES
  There shall be three classes of formal data parameters:
  
      input parameters, which act as constants that are initialized to the value of corresponding actual parameters at the time of call,
  
      input-output parameters, which enable access and assignment to the corresponding actual parameters, and
  
      output parameters, which act as local variables whose values are transferred to the corresponding actual parameter only at the time of normal exit. In the latter two cases the corresponding actual parameter must be a variable or an assignable component of a composite type.
  
  7I.  RESTRICTIONS TO PREVENT ALIASING
  
  Aliasing (i.e., multiple access paths to the same variable from a given scope) shall not be permitted. In particular, a variable may not be used as two output arguments in the same call to a procedure, and a nonlocal variable that is accessed or assigned within a procedure body may not be used as an output argument to that procedure.
These are very reasonable!

7I is important (in the mutable case), and solving the problem that bought forth this rule is the core idea behind Rust.

7F draws useful semantic distinctions: Output parameters are return values. Input-output parameters are mutable references. Input parameters act as a copy or readonly reference – which are equivalent if you can't mutate or observe mutation through readonly references.

Your complaint is that Rust draws a distinction between whether input parameters are implemented via a copy or a reference, but Rust draws many more distictions here – arbitrarily many so, because it's part of the type system. This makes the system both applicable everywhere instead of only in function parameters and allows expressing more different semantics.

For example I have a hard time seing how one would, while following the Ironman requirements, distinguish parameter whos type is "dynamic array of elements of T" from one of type "dynamic array of elements of type mutable reference to T". Likewise you can define a record that holds a mutable reference and an immutable reference, or define a new reference type (such as a reference counted pointer) without new language support.

I said that passing a copy and a readonly reference are equivalent. However, in Rust there are no readonly references. Rather, there are non-aliasing references, which allow mutation, and aliasing references, which by default don't allow mutation – but types can make use of interior mutability to allow mutation through aliased references according to they rules they need. For example you can access data protected by a mutex if and only if you hold the lock, which means that even if other references to the mutex exist, there are no other references to the inner data.



Just to be clear, not your parent, and not really a fan of STEELMAN. But.

> Input-output parameters are mutable references.

In/out can also be by value, it would copy any updates back after the call.

> Input parameters act as a copy or readonly reference

This is what your parent is getting at: the idea is that in/out/inout describe intent, not mechanism. The compiler chooses the mechanism for you.

I think in a language that's intended for lower-level tasks, describing mechanism is important. That said, outside of STEELMAN, there's an argument to be made for in/out/inout, and in fact, there's been some discussion over the years, for example, &uninit T as a sort of variant of out.

> For example I have a hard time seing how one would, while following the Ironman requirements,

You're right. STEELMAN updated this section to say

  7I. Restrictions to Prevent Aliasing. The language shall attempt to prevent aliasing (l.e., multiple access paths to the same variable or record component) that is not intended, but shall not prohibit all aliasing. Aliasing shall not be permitted between output parameters nor between an input-output parameter and a nonlocal variable. Unintended aliasing shall not be permitted between input-output parameters. A restriction limiting actual input-output parameters to variables that are nowhere referenced as nonlocals within a function or routine, is not prohibited. All aliasing of components of elements of an indirect type shall be considered intentional.
Ada has "access types," which are pointers. You can declare them as aliased or not. Via this mechanism, you can pass both an aliased variable as an in parameter, and an access type that points to it as an in out, and still modify the variable, even though it's in. This will give you surprising behavior, but it's not UB, so you get "oh hey that number is not what it should be" not "this means half your program is optimized away.

However, unlike Rust, Ada does do strict aliasing, and so you can also get miscompilations that way. It requires the moral equivalent of unsafe, though. See https://gcc.gnu.org/onlinedocs/gcc-7.5.0/gnat_ugn/Optimizati... for an example.

Ada does prevent data races, because it has a built-in task system, and that task system only allows multiple tasks to access "protected objects" that are basically RWLock<T> in Rust terms.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: