www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What ever happened to move semantics?

reply Manu <turkeyman gmail.com> writes:
I've started writing some code for the first time in years; and the time
away and distance really gave me some fresh perspective.

I started writing D because that's what I prefer, and I was shaken with the
realisation that the situation is just so much worse than I was ever
willing to admit to myself.
This is a small greenfields app, and I want to develop it quickly... so I
told myself that I'd try and use phobos.
...but it needs to run on microcontrollers, so I need to compile tiny code
and avoid GC and stuff. Better-C seems like a good plan, except that phobos
is just not compatible.
So, I figured I'd just use phobos and worry about fixing up allocation
semantics later when I want to release on hardware, so I can dev the
business logic on PC quickly.
Seems like a plan, except phobos is just BAD. API's are inconsistent and
mostly horrible. Nothing is intuitive, and also I realise I'm writing
myself into a huge overhaul when I want to forge a microcontroller build
later.

The biggest slap in the face though, which is honestly unexcusable 20 years
on, is that D still has no real meaningful container library! How can a
modern language not have a library of containers that let me organise my
data?!

I realised that the best and most usable containers are actually the stdcpp
containers I was implementing years ago! But the thing that held up that
work was that we STILL don't have any way to implement move semantics!
There was a SAOC project to implement rvalue references which would open D
up to implementing container libraries; but sadly the author just
disappeared one day never to be seen again. Nobody else ever moved on that
issue.

Walter: It's been too long, there are still no containers, which is
embarrassing and stdcpp is still blocked on this. I strongly urge you to
drop whatever you are doing and finally implement an rvalue reference so we
can *finally *implement move semantics, and with that, we can have a
container library worthy of the language (I'll even start by finishing
stdcpp which is blocked on that issue).
Feb 26
next sibling parent Mike Shah <mshah.475 gmail.com> writes:
On Tuesday, 27 February 2024 at 02:28:32 UTC, Manu wrote:
 I've started writing some code for the first time in years; and 
 the time away and distance really gave me some fresh 
 perspective.

 I started writing D because that's what I prefer, and I was 
 shaken with the
 realisation that the situation is just so much worse than I was 
 ever
 willing to admit to myself.
 This is a small greenfields app, and I want to develop it 
 quickly... so I
 told myself that I'd try and use phobos.
 ...but it needs to run on microcontrollers, so I need to 
 compile tiny code
 and avoid GC and stuff. Better-C seems like a good plan, except 
 that phobos
 is just not compatible.
 So, I figured I'd just use phobos and worry about fixing up 
 allocation
 semantics later when I want to release on hardware, so I can 
 dev the
 business logic on PC quickly.
 Seems like a plan, except phobos is just BAD. API's are 
 inconsistent and
 mostly horrible. Nothing is intuitive, and also I realise I'm 
 writing
 myself into a huge overhaul when I want to forge a 
 microcontroller build
 later.

 The biggest slap in the face though, which is honestly 
 unexcusable 20 years on, is that D still has no real meaningful 
 container library! How can a modern language not have a library 
 of containers that let me organise my data?!

 I realised that the best and most usable containers are 
 actually the stdcpp containers I was implementing years ago! 
 But the thing that held up that work was that we STILL don't 
 have any way to implement move semantics! There was a SAOC 
 project to implement rvalue references which would open D up to 
 implementing container libraries; but sadly the author just 
 disappeared one day never to be seen again. Nobody else ever 
 moved on that issue.

 Walter: It's been too long, there are still no containers, 
 which is embarrassing and stdcpp is still blocked on this. I 
 strongly urge you to drop whatever you are doing and finally 
 implement an rvalue reference so we can *finally *implement 
 move semantics, and with that, we can have a container library 
 worthy of the language (I'll even start by finishing stdcpp 
 which is blocked on that issue).
I long suspected the lack of different containers is because the built-in fixed-size array, dynamic array, and associative array are sufficient for getting most applications up and running quickly for those who are fine with linking in Phobos and D-Runtime. Perhaps in the same way that many folks using Python stick to lists and dictionaries for any small program. For folks with C++ backgrounds, more containers for performance trade-offs would indeed be nice to have to select from (e.g. unordered_map, map, etc.). std.container (as you probably know, but others may not so I'll link it here - https://dlang.org/phobos/std_container.html) does have a few data structures like std.array as a start (API similar to std::vector). I agree the container list could be expanded and would benefit greatly from move semantics (i.e. using rvalue reference) -- this is probably the one thing I miss from D in C++. I don't know the history regarding rvalue reference with D, but I did find [1][2] useful. Read through the dip and watching the talk as we speak. Curious your thoughts on containers: 1. Should we effectively mirror the cpp containers? 2. Add more specific ones (e.g. std.container.concurrent.array? std.container.lockfree, std.container.intrusive)? 3. Add more (e.g. std.graph, std.directedgraph, etc.)? ^ Question above are to help kickstart some of the discussion as I hear about rumblings of Phobos 3. Regarding the API: I believe there are rumblings of allocators here. Wonder if we again take the C++ approach? e.g. `auto arr = Array!(int, allocatorType)(1,2,3); // second parameter we specify allocator type/policy/strategy. I also *think* some proposal is in progress here -- others can comment to confirm. [1] Found your previous dip here. https://github.com/dlang/DIPs/blob/725541d69149bc85a9492f7df07360f8e2948bd7/DIPs/DIP1016.md [2] Andrei's talk 2019 talk: https://www.youtube.com/watch?v=aRvu2JGGn6E
Feb 26
prev sibling next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
We are aware of all of this.

Move semantics are still on someones radar (I've forgotten who), it was 
wanted by Weka.io. The problem is the design of move semantics itself 
and motivation. Neither of which are contingent on Walter. Wait for DIP 
process to reopen shortly.

A new standard library is currently in the works of being planned.
It is lead by Adam Wilson who has buy in from both Walter and Atila.
This ties into a new planned edition system for ensuring stability of 
language.

https://github.com/LightBender/PhobosV3-Design

Shared library support has improved quite a bit, dmd is still WIP for 
druntime, but Rainer has gotten dmd's core capabilities working now.
I believe I have successfully designed the user experience surrounding 
it, although that has been implemented or accepted just yet. Which will 
help prevent linker errors in all common scenarios.
Feb 26
parent Mike Parker <aldacron gmail.com> writes:
On Tuesday, 27 February 2024 at 06:19:53 UTC, Richard (Rikki) 
Andrew Cattermole wrote:

 A new standard library is currently in the works of being 
 planned.
 It is lead by Adam Wilson who has buy in from both Walter and 
 Atila.
 This ties into a new planned edition system for ensuring 
 stability of language.

 https://github.com/LightBender/PhobosV3-Design
I'll add that containers are specifically a goal right now. Robert Schadek's upcoming DConf Online talk is related to that. He's volunteered to head up work on a container project, and Steven Schveighoffer has offered to help where he can. There's also somewhat related work on allocators that Atila and Paul Backus are heading up.
Feb 27
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Hi Manu! Good to hear from you!

We do have a DIP on move semantics I wrote a while back:

https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

but other demands always seem to get in the way. I'd appreciate your advice on 
the DIP.

 I realised that the best and most usable containers are actually the stdcpp
 containers I was implementing years ago!
It's not surprising that the containers you worked on are the most useful to you! I'm curious about what containers are needed for a microcontroller? Since an overhaul of Phobos is underway, I'm especially interested in your takes on unintuitive/horrible aspects. I don't want to bias your response with my thoughts on the matter.
Feb 26
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 27 February 2024 at 07:20:46 UTC, Walter Bright wrote:

 We do have a DIP on move semantics I wrote a while back:

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

 but other demands always seem to get in the way.
ImportC was not a demand!
Feb 26
next sibling parent reply Manu <turkeyman gmail.com> writes:
On Tue, 27 Feb 2024 at 18:01, Max Samukha via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Tuesday, 27 February 2024 at 07:20:46 UTC, Walter Bright wrote:

 We do have a DIP on move semantics I wrote a while back:

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

 but other demands always seem to get in the way.
ImportC was not a demand!
=F0=9F=91=86=F0=9F=91=86=F0=9F=91=86 I've been really hammering the importance of this issue for *at least* 10 years. I'm so tired of hearing myself rant, I'm just way done. I'm still as convinced as ever that the single most important thing you or anybody could be doing for the language, is fixing the move hole. I'd like to be on the DIP review panel. There are several issues that stand out to me, which I think I'll need to sleep on to digest. value for extern(C++) is a gigantic breaking change. Approaching it from that angle is an interesting idea, but I wonder if it could be really noisy. Most code passes struct/class by ref in C++; by-val structs are fairly rare, so the idea might actually work out. If symmetry or someone has a small budget, we should fly a small group of key individuals into a small room, lock the doors for a few days, and just get it done.
Feb 27
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/27/2024 4:16 AM, Manu wrote:
 I've been really hammering the importance of this issue for /at least/ 10
years. 
 I'm so tired of hearing myself rant, I'm just way done. I'm still as convinced 
 as ever that the single most important thing you or anybody could be doing for 
 the language, is fixing the move hole.
 I'd like to be on the DIP review panel. There are several issues that stand
out 
 to me, which I think I'll need to sleep on to digest.
I'm looking forward to your thoughts on it. (Anyone can review any DIP.)
  value for extern(C++) is a gigantic breaking change. Approaching it from that 
 angle is an interesting idea, but I wonder if it could be really noisy. Most 
 code passes struct/class by ref in C++; by-val structs are fairly rare, so the 
 idea might actually work out.
Proof that you read the dip!
 If symmetry or someone has a small budget, we should fly a small group of key 
 individuals into a small room, lock the doors for a few days, and just get it
done.
Come to Seattle!
Feb 27
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/27/24 13:16, Manu wrote:
 I'm still as convinced as ever that the single most important thing you 
 or anybody could be doing for the language, is fixing the move hole.
FWIW I have been pushing this a couple times at the DLF meetings, but in the end somebody will have to put in the work to implement it in the compiler and I cannot spend the time required for that atm. The move hole is also an issue for tuple unpacking though.
Feb 27
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/27/2024 4:42 PM, Timon Gehr wrote:
 FWIW I have been pushing this a couple times at the DLF meetings, but in the
end 
 somebody will have to put in the work to implement it in the compiler and I 
 cannot spend the time required for that atm.
 
 The move hole is also an issue for tuple unpacking though.
Reviewing the DIP would be a big help if that can work for you. https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md
Feb 27
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/28/24 02:06, Walter Bright wrote:
 On 2/27/2024 4:42 PM, Timon Gehr wrote:
 FWIW I have been pushing this a couple times at the DLF meetings, but 
 in the end somebody will have to put in the work to implement it in 
 the compiler and I cannot spend the time required for that atm.

 The move hole is also an issue for tuple unpacking though.
Reviewing the DIP would be a big help if that can work for you. https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md
Sure! A lot of good stuff in there. Here's my review. Points 1 to 15 respond to the DIP contents. The main issue I see is the way move construction and assignment are declared by special-casing existing syntax that already means something else _and changing its observable behavior_. To fix this, I think there should be separate syntax for suppressing the destructor call. Furthermore, partial moving in general does not work in the way it is specified in the DIP, it bypasses the destructor of the enclosing struct without participation of that struct. Point 16 to 18 point out things that are missing from the DIP. The main issue I see here is that destructuring is missing from the DIP. This is crucial in order to be able to transform data from one type into data from another type while using only moves and no copies or destruction. 1. Regarding last use:
 ```d
 S s;
 f(s); // copy
 f(s); // copy
 f(s); // move
 ```
It would be useful to show examples with dynamic control flow (edit: I see some examples occur later too), such as: ```d S s foreach(i;0..3){ f(s); // ? } ``` I assume the line marked "?" will always copy? Maybe it would be better to allow implementation-defined copy elision (also see 11.). ```d S s; f(s); // copy f(s); // ? if(uniform(0,2)) return; f(s); // move ``` I assume the line marked "?" will always copy? Maybe it would be better to allow implementation-defined copy elision (also see 11.). 2. Regarding Existing State in D: - It would make sense to elaborate on ` disable`d copy constructors. This is similar to not implementing the `Copy` trait in Rust. The resulting values can only be moved. - In D, you can also have a `private` destructor. As far as I can tell, this is currently useless, but with move semantics this can be used to enforce explicit destruction via move, which is a nice way to design a library interface. 3. Regarding declaration syntax of Move Constructors and Move Assignment Operators I would highly recommend to use a distinct syntax for suppressing destruction of the argument. I will argue here specifically for the case of Move Constructors, but Move Assignment operators have exactly the same issue.
 
 A Move Constructor is a struct member constructor that moves, rather than
copies, the argument corresponding to its first parameter into the object to be
constructed. The argument is invalid after this move, and is not destructed.
 
 A Move Constructor for struct S is declared as:
 
 ```d
 this(S s) { ... }
 ```
This is a breaking language change. Also, consider ```d struct S{ ... this(T)(T t){ ... } ... } ``` This constructor will be a move constructor iff T=S. Therefore, that the destructor is not called on the argument in some cases may be very surprising to programmers. A similar example is this one ```d struct S{ ... this(T...)(S s, T args){ ... } ... } ``` Here, the constructor is a move constructor iff no additional `args` are passed. Overall, the proposed syntax introduces a surprising special case. Also, what is the syntax for a copy constructor? Would it be `this(ref S s){ ... }` ? 4. Regarding `nothrow` on Move Constructors and Move Assignment Operators.
 The Move Constructor is always nothrow, even if nothrow is not explicitly
specified. A Move Constructor that throws is illegal.
This special case should be motivated in the DIP. I assume the motivation is that because the argument is not destructed, throwing is particularly error-prone here. In general, I would advise against built-in requirements on specified attributes unless absolutely necessary. 5. Regarding Default Move Constructor
 If a Move Constructor is not defined for a struct that has a Move Constructor
in one or more of its fields, a default one is defined, and fields without a
Move Constructor are moved using a bit copy.
This is missing a specification of what the default move constructor does. (I assume it is implemented as a move for each field, in lexical order, where fields without a Move Constructor are moved using a bit copy.) 6. Regarding Default Move Constructor and Default Move Assignment Operator.
 If a Move Constructor is not defined for a struct that has a Move Assignment
Operator, a default Move Constructor is defined and implemented as a move for
each of its fields, in lexical order.
 
This generated move constructor will often do the wrong thing. A correct way to do it would be to default-initialize a new instance and then call the Move Assignment Operator on it. It is also worth considering if instead, a Move Constructor Operator should not just be required to be defined explicitly in any struct that has an explicit Move Assignment Operator defined.
 If a Move Assignment Operator is not defined for a struct that has a Move
Assignment Operator in one or more of its fields, a default Move Assignment
Operator is defined, and fields without a Move Assignment Operator are moved
using a bit copy.
 
 If a Move Assignment Operator is not defined for a struct that has a Move
Constructor, a default Move Assignment Operator is defined and implemented as a
move for each of its fields, in lexical order.
This generated move assignment operator will usually do the wrong thing. A correct but inefficient way to do it would be to destroy the current object and reconstruct it using the Move Constructor. It is also worth considering if instead, a Move Assignment Operator should not just be required to be defined explicitly in any struct that has an explicit Move Constructor defined. 7. Regarding EMO
 An EMO is a struct that has both a Move Constructor and a Move Assignment
Operator. An EMO defaults to exhibiting move behavior when passed and returned
from functions rather than the copy behavior of non-EMO objects.
This definition is not self-contained and should therefore refer to the discussion further below for clarification. 8. Regarding Move Ref
 A Move Ref is a parameter that is a reference to an EMO. (The ref is not used.)
For small structs, the additional indirection from the implicit reference will introduce overhead. 9. Regarding NRVO of EMO objects
 If NRVO cannot be performed, s is copied to the return value on the caller's
stack.
This is surprising to me. I would have expected `s` to be moved to the return value on the caller's stack instead. 10. Regarding Returning an EMO by Move Ref This is too cute, because it changes the meaning of `return` in one specific special case. Consider: ```d struct S{ int* ptr; this(S s){ this.ptr=s.ptr; } void opAssign(S s){ this.ptr=s.ptr; } } S func(return S s){ return S(s); } ``` The `return` annotation is needed because the pointer again appears in the return value. Note that this is a simplified example, but we could think of similar ones with multiple involved pointers that need to be permuted (though I don't know how to implement that without destructuring or destruction). 11. Regarding Copy Elision Maybe it would be better to specify explicitly that an implementation is allowed to optimize the pattern: ```d auto s = t; // (copy) ... // arbitrary code not referring to `t` destroy(t); ``` to: ```d auto s = move(t); ``` 12. Regarding lifetimes. You make a point about nested functions and lambdas. However, this is not the only problem. Consider: ```d struct S{ int x; } int foo() safe{ S s; scope p = &s.x; bar(s); // last use of s, moved return *p; // bad memory access } ``` 13. Regarding partial move.
 Therefore, the generalized rule is that an access to an EMO field of an
aggregate will be moved only if that is the last access of the containing
variable.
This does not work. You cannot elide the entire destructor of `S` based on moving a single field of `S`. 14. Regarding Destruction This is a bit inconsistent with what was presented earlier. I agree that implementation-defined copy elision is probably a good idea (see 11.). 15. Regarding C++ interop. I do not see anything obviously wrong, except that the requirement to opt out of rvalue references seems error prone. I think Manu has more expertise here. Also, it would be good to specify ` value` as a standalone thing in the DIP, as it may be useful beyond C++ interop (also see point 8.). What is missing from the DIP? 16. Missing: Redeclaration after Move ```d S s, t; func(s); // moved, `s` no longer accessible S s = t; // explicit construction via redeclaration ``` A nice feature of this is that the type of a variable can be changed on redeclaration. Note that Rust allows this. 17. Missing: Destructuring This is partially attempted in the DIP via partial move (which does not work). However, there must be a way to implement the following: ```d struct U(T...){ T fields; } struct S(T...){ T fields; disable ~this(); ... // need support from S to bypass destructor } // fields of resulting U must be moved from the fields of S U fromS(S s){ ... } ``` 18. Missing: Moving the receiver ```d struct S{ T foo() rvalue{ ... } disable ~this(); } ``` void main(){ S s; auto t=s.foo(); // last use of s } ```
Feb 28
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/28/24 15:17, Timon Gehr wrote:
 ...
 What is missing from the DIP?
 ...
 
19. Explicit moves. There is no way to force that a given occurrence of a variable is the last use and is moved. We can use std.algorithm.move, but the DIP as specified would just move a copy if something is used again. What I would like to see is: ```d void main(){ S s; foo(move(s)); auto t=s; // error, `s` has been moved S s; // ok, can redeclare `s` } ``` Maybe there needs to be a parameter annotation that forces a move, then `move` can be implemented in terms of that.
Feb 28
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/28/2024 6:22 AM, Timon Gehr wrote:
 On 2/28/24 15:17, Timon Gehr wrote:
 ...
 What is missing from the DIP?
 ...
19. Explicit moves. There is no way to force that a given occurrence of a variable is the last use and is moved. We can use std.algorithm.move, but the DIP as specified would just move a copy if something is used again.
Forcing a move just sounds like trouble. If it is not the last use, and it is moved, then wouldn't the result be undefined behavior? Determining last use should be in the purview of the compiler, not the user, so it is reliable. The Ownership/Borrowing system does determine last use, using data flow analysis.
 What I would like to see is:
 
 ```d
 void main(){
      S s;
      foo(move(s));
      auto t=s; // error, `s` has been moved
      S s; // ok, can redeclare `s`
 }
 ```
 
 Maybe there needs to be a parameter annotation that forces a move, then `move` 
 can be implemented in terms of that.
What is the point of declaring another variable of the same name? We already disallow shadowing declarations, and that has prevented a number of bugs at least in my own code (they're very difficult to spot with a visual check).
Feb 28
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 2/28/24 21:38, Walter Bright wrote:
 On 2/28/2024 6:22 AM, Timon Gehr wrote:
 On 2/28/24 15:17, Timon Gehr wrote:
 ...
 What is missing from the DIP?
 ...
19. Explicit moves. There is no way to force that a given occurrence of a variable is the last use and is moved. We can use std.algorithm.move, but the DIP as specified would just move a copy if something is used again.
Forcing a move just sounds like trouble. If it is not the last use, and it is moved, then wouldn't the result be undefined behavior?
No, the idea is that the compiler enforces that it is indeed the last use and produces a compile-time error message if it cannot prove that it is the case.
 Determining 
 last use should be in the purview of the compiler, not the user, so it 
 is reliable.
 ...
Yes. It still holds that one may want to make sure that a value is really moved at a given point. Sometimes this matters. Anyway, this is by far not the most important point.
 The Ownership/Borrowing system does determine last use, using data flow 
 analysis.
 
 
 What I would like to see is:

 ```d
 void main(){
      S s;
      foo(move(s));
      auto t=s; // error, `s` has been moved
      S s; // ok, can redeclare `s`
 }
 ```

 Maybe there needs to be a parameter annotation that forces a move, 
 then `move` can be implemented in terms of that.
What is the point of declaring another variable of the same name?
From my previous post:
 
 16. Missing: Redeclaration after Move
 
 ```d
 S s, t;
 func(s); // moved, `s` no longer accessible
 S s = t; // explicit construction via redeclaration
 ```
 
 A nice feature of this is that the type of a variable can be changed on
redeclaration. Note that Rust allows this.
This is a relatively common idiom in languages that support moves. It is annoying if you have to invent a new name for each intermediate result. One use case would be type state: File!(FileState.flushed) file = open("file.txt"); File!(FileState.buffered) file = file.write("hello "); File!(FileState.buffered) file = file.writeln("world!"); // file.close(); // compile time error File!(FileState.flushed) file = file.flush(); file.close(); // moves "file" // file.write("hello"); // compile time error Assume you have some declarations like these and you want to comment out part of the statements. It would now be annoying to have to rename variables. I.e., you want to use the same name for different versions of the same thing, similarly to how you do not have to change the name of a variable when assigning to it.
 We already disallow shadowing declarations, and that has prevented a number 
 of bugs at least in my own code (they're very difficult to spot with a 
 visual check).
The reason why shadowing is error prone is that multiple variables with overlapping lifetimes are in scope and the compiler arbitrarily picks one of them. This case is different, as only one variable of the same name exists at any given time. This is not error prone. Requiring unique names is more error prone in this case, as you can accidentally copy an older version of a variable. Anyway, this is not the most important thing, please do check out the points I initially included in my review. This point is just something I had forgotten to include.
Feb 28
next sibling parent reply Don Allen <donaldcallen gmail.com> writes:
As I've said in previous posts, I've written a lot of Rust and 
now a fair amount of D. I prefer D as it is, now that I've solved 
some issues with my own lack of knowledge about the available 
tools.

I want to make a comment about all this chatter about move 
semantics. I freely admit that I have not read the proposal for 
doing this, if there is a fully-fleshed-out proposal, and I 
probably never will.

My issue is this: Rust has led the move semantics charge. 
Grafting move semantics onto feels to me like me-too-ism and just 
cluttering the D environment with yet another wart that it does 
not need. Move semantics in Rust results in the programmer 
becoming an key part of the memory management system, whether 
they realize it or not. Anyone who has wrestled with the borrow 
checker or gotten themselves into lifetime hell with Rust will 
understand (unless they've drunk the Kool-Aid) what I am talking 
about. Rust is a fiendishly difficult language to learn. I've 
been doing this for a very long time and have written code in 
languages most of you have never heard of. None of them were as 
hard to learn as Rust and I include Haskell, which I know well.

And what's the reward for all this diffculty? No GC. The Rust 
community's allergy to garbage collection is totally overblown, 
in my opinion. Yes, garbage collected languages are not good 
choices for embedded and/or real-time code. But ordinary 
applications? On today's multi-Ghz 32 Gb hardware? And the cost 
of avoiding garbage collection is great. You need to hear the 
observations of more people like me who know both Rust and D and 
I think the tune will be much the same -- D is much easier to 
learn and easier to use. The resulting code? D code runs fast, in 
my experience it is as fast as Rust, within a very acceptable 
tolerance. And benchmarks I've seen online confirm this. Why is 
an uninteresting language like Go so much more popular than Rust? 
It's easy to learn, gives acceptable performance, and has a rich 
environment.

So before cluttering the D environment with an unneeded me-too 
effort (my opinion!) and wasting scarce developer resources on 
this when there is so much else to be done that D needs more than 
a borrow-checker, I urge you to think very carefully about this.
Feb 28
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 2/28/24 23:01, Don Allen wrote:
 As I've said in previous posts, I've written a lot of Rust and now a 
 fair amount of D. I prefer D as it is, now that I've solved some issues 
 with my own lack of knowledge about the available tools.
 
 I want to make a comment about all this chatter about move semantics. I 
 freely admit that I have not read the proposal for doing this, if there 
 is a fully-fleshed-out proposal, and I probably never will.
 ...
Well, suit yourself.
 My issue is this: Rust has led the move semantics charge.
Linear typing is a very old idea.
 Grafting move 
 semantics onto feels to me like me-too-ism and just cluttering the D 
 environment with yet another wart that it does not need.
No, the warts are here now. Move semantics is a way to get rid of existing warts.
 Move semantics 
 in Rust results in the programmer becoming an key part of the memory 
 management system, whether they realize it or not.
It's just another tool. Use it when it is appropriate.
 Anyone who has 
 wrestled with the borrow checker or gotten themselves into lifetime hell 
 with Rust will understand (unless they've drunk the Kool-Aid) what I am 
 talking about.
Move semantics is not the same as borrowing.
 ...
 
 So before cluttering the D environment with an unneeded me-too effort 
 (my opinion!) and wasting scarce developer resources on this when there 
 is so much else to be done that D needs more than a borrow-checker, I 
 urge you to think very carefully about this.
You seem to confuse DIP1000 with move constructors. Move semantics is one of the things D needs more than a borrow checker. Anyway, move constructors _are_ among the top 10 priorities now for the language.
Feb 28
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I do share your concern, Don. I worry that we know so well how C++ does it that 
we can't conceive of a better way. If we cannot do mucho better than C++ with 
this, why are we using D?

D already does some things so much better than C++ by thinking "dammit there's 
got to be a better way than this mess!"

Things like no ADL, decent modules, simple template syntax, easy to understand 
overloading, context-free grammar, slices, etc.
Feb 28
next sibling parent reply electricface <electricface qq.com> writes:
On Thursday, 29 February 2024 at 04:24:02 UTC, Walter Bright 
wrote:
 I do share your concern, Don. I worry that we know so well how 
 C++ does it that we can't conceive of a better way. If we 
 cannot do mucho better than C++ with this, why are we using D?

 D already does some things so much better than C++ by thinking 
 "dammit there's got to be a better way than this mess!"

 Things like no ADL, decent modules, simple template syntax, 
 easy to understand overloading, context-free grammar, slices, 
 etc.
Try using DFA technology not during normal compilation in the compiler, but in an alternative checking mode that maintains compilation performance while allowing program-assisted analysis.
Feb 28
parent electricface <electricface qq.com> writes:
On Thursday, 29 February 2024 at 05:51:32 UTC, electricface wrote:
 On Thursday, 29 February 2024 at 04:24:02 UTC, Walter Bright 
 wrote:
 I do share your concern, Don. I worry that we know so well how 
 C++ does it that we can't conceive of a better way. If we 
 cannot do mucho better than C++ with this, why are we using D?

 D already does some things so much better than C++ by thinking 
 "dammit there's got to be a better way than this mess!"

 Things like no ADL, decent modules, simple template syntax, 
 easy to understand overloading, context-free grammar, slices, 
 etc.
Try using DFA technology not during normal compilation in the compiler, but in an alternative checking mode that maintains compilation performance while allowing program-assisted analysis.
Try to linguistically mark the last usage of a variable, and then develop a fast verification algorithm to improve the performance of analyzing the last usage of variables. It can also use expensive but comprehensive algorithm to guide people in marking the last usage of a variable.
Feb 29
prev sibling parent Don Allen <donaldcallen gmail.com> writes:
On Thursday, 29 February 2024 at 04:24:02 UTC, Walter Bright 
wrote:
 I do share your concern, Don. I worry that we know so well how 
 C++ does it that we can't conceive of a better way. If we 
 cannot do mucho better than C++ with this, why are we using D?

 D already does some things so much better than C++ by thinking 
 "dammit there's got to be a better way than this mess!"

 Things like no ADL, decent modules, simple template syntax, 
 easy to understand overloading, context-free grammar, slices, 
 etc.
I've expressed sentiments like this before, Walter, most recently at the time of the fork kerfuffle. I really value your careful, let's-think-it-through engineer's approach regarding the evolution of D. I'm not suggesting that the young-uns are always wrong. Quite the contrary; there are some very bright young people pushing D in various ways. The problem is that they aren't always right, which creates a wheat-from-the-chaff role for the project leader, sometimes requiring rejecting changes backed by many and other times implementing things that lack enthusiastic support, e.g, ImportC, which I hope people are beginning to understand that it is a major asset for D. I think you are doing a superb job of walking this fine line.
Mar 01
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/28/2024 1:03 PM, Timon Gehr wrote:
 No, the idea is that the compiler enforces that it is indeed the last use and 
 produces a compile-time error message if it cannot prove that it is the case.
DFA works with mathematical reliability (absent compiler bugs). The optimizer relies heavily on DFA; if DFA was unreliable the whole edifice will fall apart. Leave move vs copy to the compiler. The move vs copy/destroy choice is an optimization, and should be semantically thought of it that way.
 Yes. It still holds that one may want to make sure that a value is really
moved 
 at a given point. Sometimes this matters. Anyway, this is by far not the most 
 important point.
If more language features are needed to work around bugs in the DFA, you've failed as a language designer/implementer. :-/ Last use DFA can be implemented in a mathematically correct manner. The downside to DFA is it slows down the compiler, which concerns me in adding it to the front end semantics. I'm guessing is that's a reason why Rust has a reputation for slow compiles. (C++ doesn't have an excuse!)
 A nice feature of this is that the type of a variable can be changed on 
 redeclaration. Note that Rust allows this.
This is a relatively common idiom in languages that support moves. It is annoying if you have to invent a new name for each intermediate result. One use case would be type state: File!(FileState.flushed) file = open("file.txt"); File!(FileState.buffered) file = file.write("hello "); File!(FileState.buffered) file = file.writeln("world!"); // file.close(); // compile time error File!(FileState.flushed) file = file.flush(); file.close(); // moves "file" // file.write("hello"); // compile time error Assume you have some declarations like these and you want to comment out part of the statements. It would now be annoying to have to rename variables. I.e., you want to use the same name for different versions of the same thing, similarly to how you do not have to change the name of a variable when assigning to it.
Interesting that you bring that up. I've been slowly leaning towards the "single assignment" style, where a variable is only assigned to once, when it is initialized. Sort of a "head const" thing. Some languages enforce this (can't remember which ones). I find it makes code more readable. I get the feeling that allowing not only the contents, but the type of the variable change after re-assignment makes for less coherent code. I'm not sure why, but I find Rust code hard to read. Maybe that's part of it. I like the O/B system so much I implemented it in D ( live), not only that, but have begun adopting it as my own coding style. And it looks sooo much nicer in D syntax!
 We already disallow shadowing declarations, and that has prevented a number of 
 bugs at least in my own code (they're very difficult to spot with a visual 
 check).
The reason why shadowing is error prone is that multiple variables with overlapping lifetimes are in scope and the compiler arbitrarily picks one of them. This case is different, as only one variable of the same name exists at any given time. This is not error prone.
I've made that error myself now and then, usually as a result of moving code lines about.
 Requiring unique names is more error 
 prone in this case, as you can accidentally copy an older version of a
variable.
I can't remember making that error :-/
 Anyway, this is not the most important thing, please do check out the points I 
 initially included in my review. This point is just something I had forgotten
to 
 include.
Of course. This was just an easy to respond to issue. But a caveat. I'm kinda swamped at the moment. I'm working on some cool stuff for the upcoming DConf. I also wrote the Move/Copy/Forward DIP before I worked on the O/B system for D. The whole Move/Copy/Forward needs to be studied in the context of how it fits in with O/B. This is going to need some careful study.
Feb 28
next sibling parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 29 February 2024 at 04:17:38 UTC, Walter Bright 
wrote:
 On 2/28/2024 1:03 PM, Timon Gehr wrote:
 No, the idea is that the compiler enforces that it is indeed 
 the last use and produces a compile-time error message if it 
 cannot prove that it is the case.
DFA works with mathematical reliability (absent compiler bugs). The optimizer relies heavily on DFA; if DFA was unreliable the whole edifice will fall apart. Leave move vs copy to the compiler. The move vs copy/destroy choice is an optimization, and should be semantically thought of it that way.
Not always. Some types have to be move-only.
Feb 29
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, February 29, 2024 3:10:30 AM MST Atila Neves via Digitalmars-d 
wrote:
 On Thursday, 29 February 2024 at 04:17:38 UTC, Walter Bright

 wrote:
 On 2/28/2024 1:03 PM, Timon Gehr wrote:
 No, the idea is that the compiler enforces that it is indeed
 the last use and produces a compile-time error message if it
 cannot prove that it is the case.
DFA works with mathematical reliability (absent compiler bugs). The optimizer relies heavily on DFA; if DFA was unreliable the whole edifice will fall apart. Leave move vs copy to the compiler. The move vs copy/destroy choice is an optimization, and should be semantically thought of it that way.
Not always. Some types have to be move-only.
Yeah. One of the proposed ways to handle basic input ranges with Phobos v3 is to make them be move-only / non-copyable (and really, it's either that or force them all to be reference types, both of which come with their own issues). And if we do decide to make basic input ranges non-copyable, then explicit moves are very much going to be a thing with them, and the more type-safe we can make that the better. Being able to have the compiler do automatic moves on last use would also be huge for that, since it would drastically reduce the number of explicit moves which would be required, but there would still be times when it would have to be explicit. Also, I'm pretty sure that the folks who have been the most motivated with regards to move constructors and move semantics (e.g. the folks at Weka) have wanted it explicitly because of issues surrounding non-copyable types. So, while having moves as an optimization is important (and can be big for both performance in general and for usability with non-copyable types), I don't think that that's actually the primary motivator behind the DIP in question - at least not with regards to the folks who were pushing for it to be created in the first place. - Jonathan M Davis
Feb 29
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 2/29/24 05:17, Walter Bright wrote:
 On 2/28/2024 1:03 PM, Timon Gehr wrote:
 No, the idea is that the compiler enforces that it is indeed the last 
 use and produces a compile-time error message if it cannot prove that 
 it is the case.
DFA works with mathematical reliability (absent compiler bugs). The optimizer relies heavily on DFA; if DFA was unreliable the whole edifice will fall apart. Leave move vs copy to the compiler. The move vs copy/destroy choice is an optimization, and should be semantically thought of it that way.
 Yes. It still holds that one may want to make sure that a value is 
 really moved at a given point. Sometimes this matters. Anyway, this is 
 by far not the most important point.
If more language features are needed to work around bugs in the DFA, you've failed as a language designer/implementer. :-/ ...
This is not about catching bugs in the DFA. This is about allowing a reader of the code to not have to execute the DFA in their head in order to see that something is the last use.
 Last use DFA can be implemented in a mathematically correct manner.
No, it is undecidable. You can only implement a sound approximation.
 The 
 downside to DFA is it slows down the compiler, which concerns me in 
 adding it to the front end semantics. I'm guessing is that's a reason 
 why Rust has a reputation for slow compiles. (C++ doesn't have an excuse!)
 ...
Last-use analysis on a control-flow graph can be implemented in an efficient manner using strongly connected components. It is even easier to compute on a structured program. Rust has no goto.
 
 A nice feature of this is that the type of a variable can be changed 
 on redeclaration. Note that Rust allows this.
This is a relatively common idiom in languages that support moves. It is annoying if you have to invent a new name for each intermediate result. One use case would be type state: File!(FileState.flushed) file = open("file.txt"); File!(FileState.buffered) file = file.write("hello "); File!(FileState.buffered) file = file.writeln("world!"); // file.close(); // compile time error File!(FileState.flushed) file = file.flush(); file.close(); // moves "file" // file.write("hello"); // compile time error Assume you have some declarations like these and you want to comment out part of the statements. It would now be annoying to have to rename variables. I.e., you want to use the same name for different versions of the same thing, similarly to how you do not have to change the name of a variable when assigning to it.
Interesting that you bring that up. I've been slowly leaning towards the "single assignment" style, where a variable is only assigned to once, when it is initialized. Sort of a "head const" thing. Some languages enforce this (can't remember which ones). I find it makes code more readable. ...
My example in fact follows that style. Each variable is a separate variable that is initialized once.
 I get the feeling that allowing not only the contents, but the type of 
 the variable change after re-assignment makes for less coherent code.
 ...
This is not being proposed. Redeclaration after move is not the same as reassignment.
 I'm not sure why, but I find Rust code hard to read. Maybe that's part 
 of it.
Plenty of unfamiliar things in Rust to throw you off.
 I like the O/B system so much I implemented it in D ( live), not 
 only that, but have begun adopting it as my own coding style. And it 
 looks sooo much nicer in D syntax!
 ...
live, while sharing some concepts, is ultimately not even the same kind of thing.
 
 We already disallow shadowing declarations, and that has prevented a 
 number of bugs at least in my own code (they're very difficult to 
 spot with a visual check).
The reason why shadowing is error prone is that multiple variables with overlapping lifetimes are in scope and the compiler arbitrarily picks one of them. This case is different, as only one variable of the same name exists at any given time. This is not error prone.
I've made that error myself now and then, usually as a result of moving code lines about. ...
Well, you can always make an error by moving some code lines into a scope in which they do not fit, but in this case, if you accidentally redeclare a variable of a name that already exists, you get a compile-time error.
 Requiring unique names is more error prone in this case, as you can 
 accidentally copy an older version of a variable.
I can't remember making that error :-/ ...
Presumably you used in-place updates in cases where it would have been hard to keep track of different versions.
 
 Anyway, this is not the most important thing, please do check out the 
 points I initially included in my review. This point is just something 
 I had forgotten to include.
Of course. This was just an easy to respond to issue. ...
I do feel like the response was shot from the hip.
 But a caveat. I'm kinda swamped at the moment. I'm working on some cool 
 stuff for the upcoming DConf.
Of course! :)
 I also wrote the Move/Copy/Forward DIP 
 before I worked on the O/B system for D. The whole Move/Copy/Forward 
 needs to be studied in the context of how it fits in with O/B. This is 
 going to need some careful study.
Sure. (This was also one of the things I pointed out.)
Feb 29
prev sibling parent Dukc <ajieskola gmail.com> writes:
On Thursday, 29 February 2024 at 04:17:38 UTC, Walter Bright 
wrote:
 DFA works with mathematical reliability (absent compiler bugs). 
 The optimizer relies heavily on DFA; if DFA was unreliable the 
 whole edifice will fall apart. Leave move vs copy to the 
 compiler. The move vs copy/destroy choice is an optimization, 
 and should be semantically thought of it that way.
I'm a bit worried this would lead to implementation-defined behaviour on whether uses of a struct with a disabled copy constructor would compile. But otherwise I agree with this. Well, the spec currently doesn't allow these elision in all cases, for example it says: ```D struct A { this(ref return scope A another) {} } void fun(A a) {} void main() { A a; fun(a); // copy constructor gets called } ``` ...but I tend to agree it should be implementation-defined whether the copy constructor is called here.
Feb 29
prev sibling next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 29/02/2024 3:17 AM, Timon Gehr wrote:
 12. Regarding lifetimes.
 
 You make a point about nested functions and lambdas. However, this is 
 not the only problem. Consider:
 
 |struct S{ int x; } int foo() safe{ S s; scope p = &s.x; bar(s); // last 
 use of s, moved return *p; // bad memory access }|
I've been exploring this problem recently with isolated. What this suggests to me is that all move operator supporting types are implicitly isolated. Both s and p would belong to the same subgraph, when s gets moved, the subgraph that s and p are in would get invalidated making the return not possible. Interesting, there appears to be some inter-relationship here that I had not considered.
Feb 28
prev sibling next sibling parent Max Samukha <maxsamukha gmail.com> writes:
On Wednesday, 28 February 2024 at 14:17:44 UTC, Timon Gehr wrote:

 - It would make sense to elaborate on ` disable`d copy 
 constructors. This is similar to not implementing the `Copy` 
 trait in Rust. The resulting values can only be moved.
Also, no discussion of ` disable`d move constructors.
Feb 28
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, February 28, 2024 7:17:44 AM MST Timon Gehr via Digitalmars-d 
wrote:
 A Move Constructor is a struct member constructor that moves, rather than
 copies, the argument corresponding to its first parameter into the object
 to be constructed. The argument is invalid after this move, and is not
 destructed.

 A Move Constructor for struct S is declared as:

 ```d
 this(S s) { ... }
 ```
This is a breaking language change. Also, consider ```d struct S{ ... this(T)(T t){ ... } ... } ``` This constructor will be a move constructor iff T=S. Therefore, that the destructor is not called on the argument in some cases may be very surprising to programmers.
I've already run into issues trying to add copy constructors at work where code broke because it had declared this(T)(T t) { ...} and that then conflicted with a copy constructor which was automatically added when I added a copy constructor to the type of one its member variables. Fortunately, the result was a compiler error, and I could then add a template constraint to avoid having the templated constructor take the type that it was constructing, but the risks with something like this suddenly being turned into a move constructor certainly should be considered. In some cases, such constructors likely work just fine when given a variable of the type that they're constructing, whereas in others, they're probably never actually used that way and wouldn't compile if you tried. So, if such a constructor became a move constructor, a type that was perfectly moveable before could become an error to try to move - or do something that is very much not a move if the constructor happens to compile when given a variable of the type that it's constructing, but it does something rather different from a move. Of course, to an extent, the same could be said of this(S s) { ... } though I would guess that in most cases, having that turned into a move constructor would work, whereas a templated constructor would be much more likely to not work with the type being constructed. Another idea here would be that if we required something like an attribute on the constructor - e.g. movecons - then the compiler could not only see that you intended to make it a move constructor, but it could give you an error if it didn't have the appropriate signature. I'm not sure that we really want to go that route, but if we did require an attribute, at least we wouldn't accidentally have existing constructors turn into move constructors - and having the compiler verify for you that it's treating it as a move constructor could be valuable.
 4. Regarding `nothrow` on Move Constructors and Move Assignment Operators.

 The Move Constructor is always nothrow, even if nothrow is not explicitly
 specified. A Move Constructor that throws is illegal.
This special case should be motivated in the DIP. I assume the motivation is that because the argument is not destructed, throwing is particularly error-prone here. In general, I would advise against built-in requirements on specified attributes unless absolutely necessary.
Yeah. I don't know if it ever makes sense for move constructors to throw (e.g. arguably it shouldn't be allowed destructors to throw, though IIRC, it is currently allowed). That being said, IMHO, we should be absolutely minimizing how much we require _any_ kind of function attribute. It causes all kinds of trouble for code that doesn't want to (or can't) use them. At least with nothrow, you can do something like catch the exception and then assert false, but IMHO, in general, core language functionality should not require specific attributes. As it is, I'm having quite a bit of trouble with copy constructors and attribute requirements (e.g. getting compilation errors with regards to druntime code that insists on pure), and I need to put together some bug reports on it. Attributes can be great in theory, but they simply don't work in many cases with complex code. - Jonathan M Davis
Feb 28
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On Tue, 27 Feb 2024 at 22:16, Manu <turkeyman gmail.com> wrote:

 On Tue, 27 Feb 2024 at 18:01, Max Samukha via Digitalmars-d <
 digitalmars-d puremagic.com> wrote:

 On Tuesday, 27 February 2024 at 07:20:46 UTC, Walter Bright wrote:

 We do have a DIP on move semantics I wrote a while back:

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

 but other demands always seem to get in the way.
ImportC was not a demand!
=F0=9F=91=86=F0=9F=91=86=F0=9F=91=86 I've been really hammering the importance of this issue for *at least* 10 years. I'm so tired of hearing myself rant, I'm just way done. I'm still =
as
 convinced as ever that the single most important thing you or anybody cou=
ld
 be doing for the language, is fixing the move hole.
 I'd like to be on the DIP review panel. There are several issues that
 stand out to me, which I think I'll need to sleep on to digest.

  value for extern(C++) is a gigantic breaking change. Approaching it from
 that angle is an interesting idea, but I wonder if it could be really
 noisy. Most code passes struct/class by ref in C++; by-val structs are
 fairly rare, so the idea might actually work out.

 If symmetry or someone has a small budget, we should fly a small group of
 key individuals into a small room, lock the doors for a few days, and jus=
t
 get it done.
I'd like to see the DIP address this idiom (which Scott Meyers termed a 'universal reference'), which is ESSENTIAL in extern(C++) and must be covered or practically no C++ code can ever be represented: template<T> void fun(T&& universalRef); In this case; the template will be instantiated as `Ty&&` or `const Ty&` depending on the rvalue-ness of the argument supplied at the call site. I think `auto ref` needs to be covered by the DIP, and probably considered with respect to this point.
Feb 27
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/27/2024 4:45 AM, Manu wrote:
 I'd like to see the DIP address this idiom (which Scott Meyers termed a 
 'universal reference'), which is ESSENTIAL in extern(C++) and must be covered
or 
 practically no C++ code can ever be represented:
    template<T> void fun(T&& universalRef);
 In this case; the template will be instantiated as `Ty&&` or `const Ty&` 
 depending on the rvalue-ness of the argument supplied at the call site.
 
 I think `auto ref` needs to be covered by the DIP, and probably considered
with 
 respect to this point.
Yeah, you're right.
Feb 27
prev sibling parent reply Dakota <dakota gmail.com> writes:
On Tuesday, 27 February 2024 at 07:56:44 UTC, Max Samukha wrote:
 On Tuesday, 27 February 2024 at 07:20:46 UTC, Walter Bright 
 wrote:

 We do have a DIP on move semantics I wrote a while back:

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

 but other demands always seem to get in the way.
ImportC was not a demand!
If you use D with betterC like c (not cpp), ImportC will work very well. If you want to use well maintained C library (some of them take hundreds of thousands of manpower hours), you will need betterC. And some library even write by cpp, still provide C interface for easy integration with other language like: rust, ruby, lua, php, java. For me ImportC is more important than DIP1040, it open the doors for very many possibilities.
Feb 27
parent Max Samukha <maxsamukha gmail.com> writes:
On Wednesday, 28 February 2024 at 06:06:23 UTC, Dakota wrote:


 For me ImportC is more important than DIP1040, it open the 
 doors for very many possibilities.
I'm resorting to an argumentum ad industriam: move constructors have been explicitly demanded for at least a decade by the prominent industrial users.
Feb 28
prev sibling next sibling parent reply zjh <fqbqrr 163.com> writes:
On Tuesday, 27 February 2024 at 02:28:32 UTC, Manu wrote:
 Walter: It's been too long, there are still no containers, 
 which is embarrassing and stdcpp is still blocked on this.
You're absolutely right, it's just that there's no `container library`, and `'stdcpp'` is a bit unstable.
Feb 26
parent reply Guillaume Piolat <first.name gmail.com> writes:
On Tuesday, 27 February 2024 at 07:44:26 UTC, zjh wrote:
 On Tuesday, 27 February 2024 at 02:28:32 UTC, Manu wrote:
 Walter: It's been too long, there are still no containers, 
 which is embarrassing and stdcpp is still blocked on this.
You're absolutely right, it's just that there's no `container library`, and `'stdcpp'` is a bit unstable.
Trying to build that at: https://github.com/Inochi2D/numem And indeed we hit the lack of move semantics, as it's possible to make a vector<shared_ptr!T> but of course vector<unique_ptr!T> encounters issues (it's very much like trying to do a vector of auto_ptr in C++98). Curious what Robert will say about how to make proper containers.
Feb 27
parent zjh <fqbqrr 163.com> writes:
On Tuesday, 27 February 2024 at 10:57:00 UTC, Guillaume Piolat 
wrote:
 Trying to build that at:
 https://github.com/Inochi2D/numem
Thank you for your infomation.
Feb 27
prev sibling parent reply Sergey <kornburn yandex.ru> writes:
On Tuesday, 27 February 2024 at 02:28:32 UTC, Manu wrote:
 Walter: It's been too long, there are still no containers,
Btw will be interesting to know the list of containers you demand. Because many times people spoke about containers, but there are hundreds of them (as Mike mentioned some families. I would also add probabilistic containers to the party).. People from industry are usually talking like "containers from std are good only for simple things. If you want to solve some real and hard problem - you have to implement your specific/custom version of the container exactly for your particular problem set". So containers from std should be just simple to use and integrated well.. but if you need performance/low memory footprint, you will implement it from scratch anyway. And I think it is worth to mention https://code.dlang.org/packages/ikod-containers - nogc, safe containers that worked well for me. It has a few structures, but could be base for other implementations. Rikki: This answer sounds like "come back in 10 years" :)
Feb 27
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 27/02/2024 9:44 PM, Sergey wrote:
  Rikki:
 
 This answer sounds like "come back in 10 years" :)
Lol yes, if you want it to be all tidy with answers to many use cases with clear solutions it's like that. If however you want hope that things are going to change and we won't keep doing what we've done, then it is hopeful too!
Feb 27
prev sibling parent reply Danilo <codedan aol.com> writes:
On Tuesday, 27 February 2024 at 08:44:59 UTC, Sergey wrote:
 And I think it is worth to mention 
 https://code.dlang.org/packages/ikod-containers - nogc, safe 
 containers that worked well for me. It has a few structures, 
 but could be base for other implementations.
Maybe [tanya](https://code.dlang.org/packages/tanya) can help as well, as package or base.
Feb 27
parent Danilo <codedan aol.com> writes:
On Tuesday, 27 February 2024 at 08:56:10 UTC, Danilo wrote:
 On Tuesday, 27 February 2024 at 08:44:59 UTC, Sergey wrote:
 And I think it is worth to mention 
 https://code.dlang.org/packages/ikod-containers - nogc, safe 
 containers that worked well for me. It has a few structures, 
 but could be base for other implementations.
Maybe [tanya](https://code.dlang.org/packages/tanya) can help as well, as package or base.
Forgot to add https://github.com/dlang-community/containers before pressing (Send) 😏
Feb 27