Thread overview
const in functions
Mar 12, 2023
Salih Dincer
Mar 12, 2023
Hipreme
Mar 12, 2023
Ali Çehreli
Mar 13, 2023
Salih Dincer
Mar 13, 2023
Ali Çehreli
Mar 12, 2023
FozzieBear
Mar 17, 2023
Ali Çehreli
Mar 18, 2023
Salih Dincer
Mar 13, 2023
Basile B.
March 12, 2023

Hi,

As someone who has used const very little in my life, I want to learn and ask: What are consts used in function parameters for; isn't there a copy already?

/*
 * Here's we have a class (Foo is partially simple):
 */
class Foo(T) {
  T x; // <----------- Because gonna just an int field!

  this(T n) { x = n; }

  override
  string toString() {
    import std.conv : text;
    return x.text;
  }
}
alias Int = Foo!int; // more simple...

import std.stdio : writeln;
import std.traits: isMutable;

/*
 * Let's simply use and test it!
 */
void main()
{
  auto n = new Int(41);
  auto r = (new Int(1)).inConst(n);/*
  auto r = (new Int(1)).outConst(n);
  assert( r.x == 42 &&
    !isMutable!( typeof(r) )
  );//*/

  n.writeln; // "41": It still has a value of 41.
  r.writeln; // "43": Added an extra 1 as expected!
}


// A, we can get its to guarantee us that parameters
// won't change:

auto inConst(T)(T a, const T b) // const
{ // it's not needed --^     but     ^-- why can't this be used

  a.x += 1; /* suppose it happened accidentally!
  ^-- it's already a new copy, right?//*/

  return new T(a.x + b.x); // Foo!int(43)
}


// B is used to guarantee that the returned result
// doesn't change:

const(T) outConst(T)(T a, T b)
{
  return new T(a.x + b.x);
}

In summary, we can use it in several places (in/out data, body, etc.) in a function. Is const needed if the compiler is copying? So why can't at the beginning of the body also be used and we get this error:

>

source_file.d(38): Error: function source.inConst!(Foo!int).inConst without this cannot be const
source_file.d(26): Error: template instance source.inConst!(Foo!int) error instantiating

I appreciate the answers given because they hold a very important place in my life, thank you.

SDB@79

March 12, 2023

On Sunday, 12 March 2023 at 15:09:45 UTC, Salih Dincer wrote:

>

Hi,

As someone who has used const very little in my life, I want to learn and ask: What are consts used in function parameters for; isn't there a copy already?

Const is used for you not be able to change your values inside references. This is why we have const methods for example:


    class Test
    {
        private int a;
        int getA() const {return a;}
    }
    ```

By having this property in your method, it says you're guaranteeing that Test won't be changed when this method is called, then, it'll be less time trying to find what mutated the object, so, use it as much as you can, but never overuse it since it is a pain to deal with.

Const for value types is also used for not mutating them inside the function also leading to less debugging.

Another use case for them is for compiler being able to infer some optimizations. If your variable is `immutable`, it becomes implicitly `__gshared`, which does not uses Thread Local Storage, another good thing for optimization in both memory and access.
March 12, 2023
On 3/12/23 08:09, Salih Dincer wrote:

> As someone who has used const very little in my life

You can live without 'const' until your code interacts with other people's code. For example, the following program works just fine:

struct S {
    int * p;

    void foo() {}
}

void bar(S s) {}

void main() {
    auto s = S();
    s.foo();
    bar(s);
}

Now you replace (or a user of your code replaces) 'auto' with 'const' and you will get 2 compilation errors:

    const s = S();
    s.foo();        // ERROR 1
    bar(s);         // ERROR 2

So, if your code will ever interact with other people's code it must be const-correct. If foo() is not mutating the object (including through the p member), it better be defined as 'const':

    void foo() const {}

And if bar() is not mutating the parameter, it better take as const:

void bar(const(S) s) {}

Now the code compiles.

You can (and should) use 'const' everywhere that you can because it increases the usability of code: Mutable, 'const', and 'immutable' variables can be used with code that takes by 'const'.

This is not the same with code taking by 'immutable'. It can only be used with 'immutable' variables.

Ali

March 12, 2023
On Sunday, 12 March 2023 at 15:09:45 UTC, Salih Dincer wrote:
>
> ...

So I don't agree with part of this comment (made elsewhere in this thread):

"You can live without 'const' until your code interacts with other people's code."

Code interacts with other code. Code should always be clear as to its intent. Who wrote that code, or other code that interacts with that code, is irrelvant.

As for the issue of 'const' itelf, I refer you to a thoughtful article on this matter:

(I'm not saying I agree with this article, or that I don't agree, I'm just referring it to you to make your own judgements.

http://www.jmdavisprog.com/articles/why-const-sucks.html

March 13, 2023

On Sunday, 12 March 2023 at 19:09:13 UTC, Ali Çehreli wrote:

>

In this case, using ref will increase performance while reducing the number of copies. Would it be wise to use const ref to protect the routine from ourselves or someone else? For example:

auto inConst(
  //const
  ref Int a, const ref Int b)
{
  a.x += 1;
  return Int(a.x + b.x);
}

struct Foo(T) {
  T x;
  alias x this;
  this(T n) { x = n; }
}
alias Int = Foo!int;

SDB@79

March 13, 2023
On 3/13/23 08:17, Salih Dincer wrote:

> In this case, using `ref` will increase performance while reducing the
> number of copies.

I am not sure about that. Unless there is an expensive copy construction, most objects are simple data copies.

To use 'ref' or not should be guided through semantics. Luckily, we have the -preview=in command line switch that removes the cost of copying from the equation:

  https://dlang.org/spec/function.html#in-params

Just marking the input parameters 'in' should be sufficient in most cases.

Ali

March 13, 2023

On Sunday, 12 March 2023 at 15:09:45 UTC, Salih Dincer wrote:

>

Hi,
[...]
// A, we can get its to guarantee us that parameters
// won't change:

auto inConst(T)(T a, const T b) // const
{ // it's not needed --^ but ^-- why can't this be used

Well you got the great answers to your questions already but I like to add a comment that is that

const parameters without ref are really moot.

I whish at least that they could give the guarantee to be rvalues (i.e have no address) but this is actually not the case. All the D compilers will actually copy const parameters to locals, making them lvalues, unless optimizations are requested(1).

See codegen : https://godbolt.org/z/vev1PGWh3.

This brings the idea that maybe a rvalue storage class could be added to the language.

(1): Actually DMD does not drop the copy at all, even with -O.

March 17, 2023
On 3/12/23 16:14, FozzieBear wrote:
> On Sunday, 12 March 2023 at 15:09:45 UTC, Salih Dincer wrote:
>>
>> ...
>
> So I don't agree with part of this comment (made elsewhere in this thread):
>
> "You can live without 'const' until your code interacts with other
> people's code."

My comment was purely technical. What I meant is, if the code is an island where there is no single 'const' in sight, then a D programmer can live by ignoring 'const'. At a technical level...

This is different from C++ where you have to use references to const in order to take rvalue references as parameters. Since D does not allow binding rvalues even to references to const, this is a moot point. (-preview=in does bind rvalues to references as an optimization but that fact is hidden from the programmer.)

So, my comment is correct at a technical level.

> Code interacts with other code. Code should always be clear as to its
> intent.

Absolutely. That's why I always start with 'const' (what should have been the default but is not for historical reasons):

  const foo = bar();

I change that 'const' to e.g. 'auto' as needed.

> Who wrote that code, or other code that interacts with that
> code, is irrelvant.

However, there is always the danger of going too far though. To keep the fun in the game, a programmer should be free to do whatever they want in their own code e.g. in their throw-away script programs.

Here is one line I never include in production code but I very frequently use in short programs:

import std;

Removes mental load, frees the mind, and enables flow of programming.

Some code is written for myself, not for people who are not watching over my shoulder. There is pride in caring for every character of the source code but sometimes freedom from all concerns is good too. Luckily, D makes it easy to refactor as requirements change.

Ali

March 18, 2023

On Friday, 17 March 2023 at 22:41:18 UTC, Ali Çehreli wrote:

>

My comment was purely technical. What I meant is, if the code is an island where there is no single 'const' in sight, then a D programmer can live by ignoring 'const'. At a technical level...

This is different from C++ where you have to use references to const in order to take rvalue references as parameters. Since D does not allow binding rvalues even to references to const, this is a moot point. (-preview=in does bind rvalues to references as an optimization but that fact is hidden from the programmer.)

Let's increase the level one bit...

Again we will remain within the boundaries of -preview=in, but we will learn other hidden information. Because you get interesting results when you raise the issue to function overloading. Here's what happens when you compile the following example:

https://wandbox.org/permlink/XvInUjEtMKOS0CVD

  1. I guess none of objects are created copy. Because Apple has already ref and Pineapple didn't execute ctor because of in keyword. Isn't Quince also a const but it was created when it is not a copyible object:
>

ref: Apple
in: Pinapple
const: Quince
0 copies... [Apple: paid, pinapple, quince]

  1. If there is no function that takes ref, Apple prefers const. Please just hide the function under the version block. In this case, ctor will run once:
>

const: Apple
in: Pinapple
const: Quince
1 copies... [qpple, pinapple, quince]

  1. If you replace in with scope (function 3) then pineapple and quince will also select different functions:
>

const: Apple
const: Pinapple
in: Quince
2 copies... [qpple, qinapple, puince]

With this example, it is possible to obtain dozens of results like these. I love D, D is a very powerful language.

SDB@79