## digitalmars.D.learn - questions around mutating range algorithms, const, and return ref

• aliak (48/48) Jan 28 Hello, I'm trying to write a function called "pull" that, given 2
• =?UTF-8?Q?Ali_=c3=87ehreli?= (31/71) Jan 28 I think the following trivial wrapper around std.algorithm.remove()
• aliak (25/50) Jan 29 Haha awesome! Yes that's much better :)
• Simen =?UTF-8?B?S2rDpnLDpXM=?= (18/27) Jan 29 Consider this case:
• aliak (39/55) Jan 30 Ooh my... yes I can understand that. And I guess immutable is
• Seb (2/15) Jan 29 Not too hard to fix: https://github.com/dlang/phobos/pull/6090
• aliak (2/19) Jan 30 Nice! :D
aliak <something something.com> writes:
```Hello, I'm trying to write a function called "pull" that, given 2
ranges, "pull"s the values from range 2 out of range 1. I'm not
sure if I'm doing it correctly though, and I have some questions
so any help is appreciated. This is what I have:

ref pull(R1, R2)(return ref R1 r1, R2 r2) {
import std.algorithm, std.array, std.range;
int numFound = 0;
auto r = r1.save;
ElementType!R1[] unfoundElements;
foreach (e; r) {
if (!r2.canFind(e)) {
unfoundElements ~= e;
} else {
numFound++;
}
}
moveEmplaceAll(unfoundElements, r1);
r1.popBackN(numFound);
return r1;
}

So my questions are:

1) So first of all, is there a function like this in phobos that
I can use? From what I understand phobos is supposed to be nogc
so mutating functions like these are usually in place ones?

2) Is there a way to avoid the extra "moveEmplaceAll" call
towards the end and still get the same results?

3) Is it necessary to always import std.array (or std.range:
empty, front) when doing work like this with range algorithms?
(otherwise you get no property save when an array is used as a
range)

4) is the .save necessary? My (initial) tests seem to show that
it works without, but from my understanding of what save does, it
feels like it's necessary.

5) return ref and -dip25 ... is that something that's going to
become default at some time? Is there a timeline? (also curious
about dip1000 for that matter).

6) It will not work when I pass in a range that has const
elements.

ie:

const(int)[] arr = [1, 2, 3, 4, 5];
arr.pull([2, 3]);

This I guess makes sense because moveEmplaceAll depends on
isInputRange and from my understanding, a const range cannot be
one (is that correct?). So am I correct in thinking there really
is no way around this, or is there some move/cast trickery that I
can use maybe?

Cheers,
- Ali
```
Jan 28
=?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
```On 01/28/2018 02:52 PM, aliak wrote:
Hello, I'm trying to write a function called "pull" that, given 2
ranges, "pull"s the values from range 2 out of range 1. I'm not sure if
I'm doing it correctly though, and I have some questions so any help is
appreciated. This is what I have:

ref pull(R1, R2)(return ref R1 r1, R2 r2) {
import std.algorithm, std.array, std.range;
int numFound = 0;
auto r = r1.save;
ElementType!R1[] unfoundElements;
foreach (e; r) {
if (!r2.canFind(e)) {
unfoundElements ~= e;
} else {
numFound++;
}
}
moveEmplaceAll(unfoundElements, r1);
r1.popBackN(numFound);
return r1;
}

So my questions are:

1) So first of all, is there a function like this in phobos that I can
use? From what I understand phobos is supposed to be nogc so mutating
functions like these are usually in place ones?

I think the following trivial wrapper around std.algorithm.remove()
should do:

void removeMatching(R, N)(ref R r, N needles) {
import std.algorithm : remove, canFind;
r = r.remove!(e => needles.canFind(e));
}

void main() {
auto arr = [1, 2, 3, 2, 4];
arr.removeMatching([2, 3]);
assert(arr == [1, 4]);
}

2) Is there a way to avoid the extra "moveEmplaceAll" call towards the
end and still get the same results?

I'm not convinced that unfoundElements is needed at all. :)

3) Is it necessary to always import std.array (or std.range: empty,
front) when doing work like this with range algorithms? (otherwise you
get no property save when an array is used as a range)

I think importing std.range works as well.

6) It will not work when I pass in a range that has const elements.

ie:

const(int)[] arr = [1, 2, 3, 4, 5];
arr.pull([2, 3]);

This I guess makes sense because moveEmplaceAll depends on isInputRange
and from my understanding, a const range cannot be one (is that
correct?).

Yes but your range is not const, the elements are. So, although
const(int)[] is a range, it does not have mutable elements.

So am I correct in thinking there really is no way around
this, or is there some move/cast trickery that I can use maybe?

You don't want to mutate const elements anyway. It would be breaking a
promise that other parts of the program may be depending on. I would
return a filtered-out range:

import std.algorithm;

void main() {
const(int)[] arr = [1, 2, 3, 2, 4];
auto result = arr.filter!(e => ![2, 3].canFind(e));
assert(result.equal([1, 4]));
}

If needed, the caller can easily make an array out of the filtered range
and the element types will still be const(int):

import std.range;
auto arr2 = result.array();
static assert(is(typeof(arr2) == const(int)[]));

Cheers,
- Ali

Ali
```
Jan 28
aliak <something something.com> writes:
```On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote:
I think the following trivial wrapper around
std.algorithm.remove() should do:

void removeMatching(R, N)(ref R r, N needles) {
import std.algorithm : remove, canFind;
r = r.remove!(e => needles.canFind(e));
}

Haha awesome! Yes that's much better :)

Note to self: learn to scroll down in the docs to find other
definitions. I was convinced that remove only acted on indices :p

I'm not convinced that unfoundElements is needed at all. :)

Can't argue with that :)

6) It will not work when I pass in a range that has const

elements.
ie:

const(int)[] arr = [1, 2, 3, 4, 5];
arr.pull([2, 3]);

This I guess makes sense because moveEmplaceAll depends on

isInputRange
and from my understanding, a const range cannot be one (is

that
correct?).

Yes but your range is not const, the elements are. So, although
const(int)[] is a range, it does not have mutable elements.

Ah right, yes the range is not const indeed.

You don't want to mutate const elements anyway. It would be
breaking a promise that other parts of the program may be
depending on. I would return a filtered-out range:

Right, I want to mutate the range though, not the elements. So
moving things from one location is not considered as a const
violation in c++ for eg, maybe the D way is different though? Any
reading material there?

Also hasMobileElements!(const int[]) is true, so that means I can
move elements around right?

If I can move elements, that means I should be able to move the
ones I want to the beginning, of the range, but then I'm also
unsure about how to go about changing the size of a range.

popBack on a mutable range with const data might cause
destructors to be called, so I'm going to say that should be ok
because as a user, if you're calling a range that mutates by
removing things, and if you try and access those things later
that's probably along the same lines as removing elements from a
range that you're "foreach"ing over.

And your second approach is definitely more practical I'd say,
but I'm going to go about this as a learning exercise in dealing
with mutations, and const with ranges in D.

Thanks you for the comments!
```
Jan 29
Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
```On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:
You don't want to mutate const elements anyway. It would be
breaking a promise that other parts of the program may be
depending on. I would return a filtered-out range:

Right, I want to mutate the range though, not the elements. So
moving things from one location is not considered as a const
violation in c++ for eg, maybe the D way is different though?

Consider this case:

immutable(int)[] a = [1,2,3];
immutable(int)* b = &a[1];

You're free to slice the array or pop elements off its front or
back, but if you change the order of elements, the value that b
points to will change. D does not allow this.

You can create a new array with the elements moved into the
locations you want, and with another layer of indirections
(int*[]) you can move the elements of the array without mutating
the values.

Any reading material there?

https://dlang.org/const-faq.html

Also hasMobileElements!(const int[]) is true, so that means I
can move elements around right?

hasMobileElements checks if the value of front can be moved as in
move constructors, not as in 'can be moved to a different spot in
the range'. I will admit the name does little to unconfuse this
point.

--
Simen
```
Jan 29
aliak <something something.com> writes:
```On Monday, 29 January 2018 at 12:10:16 UTC, Simen Kjærås wrote:
Consider this case:

immutable(int)[] a = [1,2,3];
immutable(int)* b = &a[1];

You're free to slice the array or pop elements off its front or
back, but if you change the order of elements, the value that b
points to will change. D does not allow this.

You can create a new array with the elements moved into the
locations you want, and with another layer of indirections
(int*[]) you can move the elements of the array without
mutating the values.

Ooh my... yes I can understand that. And I guess immutable is
implicitly convertible to const and then if you can move const
stuff then you have that problem. From the FAQ, my general
understanding is that const is there as a bridge from immutable
to mutable as a promise that the memory under this symbol will
not be altered by that reference.

While experimenting a bit I came across this though which,
currently, I do understand that value of

struct Int {
const int value;
}

void main() {
Int i0 = Int(0);
Int i1 = Int(1);
Int i2 = Int(2);
Int[3] arr0 = [i0, i1, i2];
Int[] arr1;
arr1 ~= i0;
arr1 ~= i1;
arr1 ~= i2;

arr1[0] = i0; // nope, Error: cannot modify struct arr1[0]
Int with immutable members
}

So if a struct has a struct that has any member const, then
effectively that whole struct is a const (well immutable it
seems, so not even const)? Is that really correct? What is this
sorcery? If yes I find it very surprising, but I don't think I'm
very clear on what D is trying to solve with const ... yet.

I find const a little hard to swallow so far. From what I
understand, it's good for function parameters, and as a function
attribute for functions that return temporaries, except don't use
it with ranges (i.e. don't put it on front). Actually not sure
about function attributes on second thought. So... function
parameters and local variables that you know won't change is what
I'm going to go with for now.

Also hasMobileElements!(const int[]) is true, so that means I
can move elements around right?

hasMobileElements checks if the value of front can be moved as
in move constructors, not as in 'can be moved to a different
spot in the range'. I will admit the name does little to
unconfuse this point.

I'm not following here :s If value of front can be move
constructed, then something can take it's place no?

Thank you!
```
Jan 30
=?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
```On 01/30/2018 12:17 AM, aliak wrote:

So if a struct has a struct that has any member const, then effectively
that whole struct is a const (well immutable it seems, so not even
const)?

No, it cannot be assigned but the other parts of the object can be mutated.

Is that really correct? What is this sorcery? If yes I find it
very surprising, but I don't think I'm very clear on what D is trying to
solve with const ... yet.

It's the same with C++: A type with a const member cannot have a
compiler-generated assignment operator.

I find const a little hard to swallow so far. From what I understand,
it's good for function parameters, and as a function attribute for
functions that return temporaries, except don't use it with ranges (i.e.
don't put it on front). Actually not sure about function attributes on
second thought. So... function parameters and local variables that you
know won't change is what I'm going to go with for now.

'const' as a member function attribute is meaningful: It makes the
implicit 'this' parameter const. Same as a function parameter attribute
in that regard.

Also hasMobileElements!(const int[]) is true, so that means I can
move elements around right?

hasMobileElements checks if the value of front can be moved as in move
constructors, not as in 'can be moved to a different spot in the
range'. I will admit the name does little to unconfuse this point.

I'm not following here :s If value of front can be move constructed,
then something can take it's place no?

I'm not happy that I can answer that question. At least I opened a bug
about some part of the confusion recently. :)

https://issues.dlang.org/show_bug.cgi?id=18036

Ali
```
Jan 30
aliak <something something.com> writes:
```On Tuesday, 30 January 2018 at 09:51:18 UTC, Ali Çehreli wrote:
[...]

is trying to
[...]

It's the same with C++: A type with a const member cannot have
a compiler-generated assignment operator.

Ok, that made it obvious :)

'const' as a member function attribute is meaningful: It makes
the implicit 'this' parameter const. Same as a function
parameter attribute in that regard.

[...]

I can
[...]

as in move
[...]

the
[...]

point.
[...]

constructed,
[...]

I'm not happy that I can answer that question. At least I
opened a bug about some part of the confusion recently. :)

https://issues.dlang.org/show_bug.cgi?id=18036

Ali

ah, ok. Gotcha, thanks again, Ali.
```
Jan 30
Seb <seb wilzba.ch> writes:
```On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:
On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote:
I think the following trivial wrapper around
std.algorithm.remove() should do:

void removeMatching(R, N)(ref R r, N needles) {
import std.algorithm : remove, canFind;
r = r.remove!(e => needles.canFind(e));
}

Haha awesome! Yes that's much better :)

Note to self: learn to scroll down in the docs to find other
definitions. I was convinced that remove only acted on indices
:p

Not too hard to fix: https://github.com/dlang/phobos/pull/6090
```
Jan 29
aliak <something something.com> writes:
```On Monday, 29 January 2018 at 13:55:04 UTC, Seb wrote:
On Monday, 29 January 2018 at 11:36:26 UTC, aliak wrote:
On Monday, 29 January 2018 at 06:46:26 UTC, Ali Çehreli wrote:
I think the following trivial wrapper around
std.algorithm.remove() should do:

void removeMatching(R, N)(ref R r, N needles) {
import std.algorithm : remove, canFind;
r = r.remove!(e => needles.canFind(e));
}

Haha awesome! Yes that's much better :)

Note to self: learn to scroll down in the docs to find other
definitions. I was convinced that remove only acted on indices
:p

Not too hard to fix: https://github.com/dlang/phobos/pull/6090

Nice! :D
```
Jan 30