www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Enhanced UFCS

reply xoxo <xororwr gmail.com> writes:


I’ve often found myself avoiding UFCS in D simply because I 
couldn’t use it consistently. The compiler forces me to duplicate 
functions when working with both values and pointers, which 
defeats a lot of the elegance UFCS is supposed to provide.

Here’s a minimal example:

```d
struct Vector
{
     int data;
}

void add(ref Vector self, int value)
{
     self.data += value;
}

void main()
{
     Vector v;
     Vector* ptr = &v;

     v.add(1);     // Works
     ptr.add(1);   // Error: no matching function
}
```

The ptr.add(1) line fails because UFCS doesn’t automatically 
dereference the pointer to match the ref parameter in add. To 
make it work, I’d need to either call add(*ptr, 1) or duplicate 
the function to handle Vector*. Neither option feels ideal.


Could D support automatic UFCS for pointers to structs? That is, 
allow the compiler to transform ptr.add(1) into add(*ptr, 1) if 
the function takes a ref and ptr is a pointer to the expected 
type.

This would make UFCS much more usable without changing any 
behavior for existing code. Maybe this could be enabled via auto 
ref, inout ref, or something similar if implicit dereferencing is 
too risky to do universally.


In Zig, for example, UFCS works seamlessly whether you have a 
value or a pointer — the compiler just figures it out. That kind 
of polish would be great to see in D as well.


Less boilerplate



Better parity with languages like Zig

No need to manually unwrap pointers just to get method-like syntax
May 26
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Monday, 26 May 2025 at 07:55:20 UTC, xoxo wrote:


 I’ve often found myself avoiding UFCS in D simply because I 
 couldn’t use it consistently. The compiler forces me to 
 duplicate functions when working with both values and pointers, 
 which defeats a lot of the elegance UFCS is supposed to provide.
So the biggest caveat I see is if there are multiple options. I would say this counts as a conversion for purposes of overloading. Looks good to me though, well stated. -Steve
May 26
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
I've encountered this limitation as well, and like this idea of 
more uniformity. But as Steven said, care needs to be taken with 
overloads:

```D
// foo.d
int f(S* x) => 2; // in an imported module

// bar.d
import foo;
int f(ref S x) => 1; // in this module

S* ptr;
void main()
{
     writeln(ptr.f); // 1 or 2?
}
```

On Monday, 26 May 2025 at 07:55:20 UTC, xoxo wrote:
 Here’s a minimal example:
As of 2.111, you can do `ref Vector ptr = v;` instead of using a pointer, so perhaps you can use a different motivating example.
May 26
parent Kagamin <spam here.lot> writes:
On Monday, 26 May 2025 at 14:18:36 UTC, Dennis wrote:
 As of 2.111, you can do `ref Vector ptr = v;` instead of using 
 a pointer, so perhaps you can use a different motivating 
 example.
I think, it works only for local variables. ``` char*[] args; args[0].strlen; ```
May 27
prev sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Monday, 26 May 2025 at 07:55:20 UTC, xoxo wrote:

I think overload resulation is a big old mess and even small asks will break stuff unexpectedly. This could be thrown to the bottom of the list of is 6(?) rules and then only stuff using compiles check would break, but that would reduce its scaling drasticly and would still be a fight. also: ```d struct Vector { int data; } void add(T)(ref T self, int value)//<-- { self.data += value; } void main() { Vector v; Vector* ptr = &v; v.add(1); // Works ptr.add(1); // also works } ``` Id suggest trying for either: a) reusing the property keyword for ufcs functions to modify overload resulation(in my opinion the rules are exactly backwards) b) adding a type specailization that matches for T and T* c) ufcs perfers a named argument of "self"/"ufcs"/"this"
May 26