On Wednesday, April 10, 2024 2:41:56 PM MDT Lettever via Digitalmars-d-learn 
wrote:
> ```
> import std;
>
> Nullable!int func() => 3;
> void main() {
>      Nullable!int a = 3;
>      //works fine
>      Nullable!int b = func();
>      //does not compile
> }

Technically, no implicit conversion is going on here. What's happening is
that you're effectively using an alternate syntax for construction. So,

    Nullable!int a = 3;

gets treated as

    Nullable!int a = Nullable!int(3);

And this only happens in cases where the compiler must do construction,
which is what you're doing when you declare a variable and give it a value
that isn't the exact same type as the variable, since construction is the
only thing that it can do in that case. It's either that or requiring that
you call the constructor explicitly, and for better or worse, the compiler
interprets it as a constructor call without the explicit constructor call.
But it only works, because it's explicit that a variable is being declared,
and therefore it must be the case that you're constructing it when you use =
to give it a value.

On the other hand, when you're returning a value from a function, you're not
telling the compiler to construct anything, because unlike when declaring a
variable, construction isn't necessary. So, at that point, the value being
returned has to either have the same type as the return type, or it needs to
implicitly convert to the return type, and int does not implicitly convert
to Nullable!int, so you get an error.

The only implicit conversions that the language has for user-defined types
are

1. When a type has an alias this, it defines an implicit conversion _to_ the
type that the alias evaluates to.

2. When a type has a base type (e.g. a class or an enum), then it will
implicitly convert to its base type.

There is no way to implicitly convert via construction, since having that
makes it harder to keep track of what happens when calling a function. This
can get very bad in C++ given that it allows you to declare a type to
implicitly convert to, and it has implicit construction. They had to add the
ability to mark parameters as explicit to block implicit conversions,
because it causes enough problems. So, Walter decided to simplify how
implicit conversions work in D, and we don't have implicit construction.

Of course, there are times when the lack of implicit construction can get
annoying (and Nullable is a prime example of that), but other problems are
reduced as a result.

For Nullable, the solution is to use the nullable helper function so that
you don't have to repeat the type. E.G.

Nullable!int func() => nullable(3);

or

auto func() => nullable(3);

And in many cases, rather than implicitly calling the constructor when
declaring a variable, it's arguably better to just use auto with an explicit
constructor call (and it definitely helps with Nullable). E.g.

auto a = nullable(3);

allows you to not bother giving the explicit type of the Nullable at all.

- Jonathan M Davis



Reply via email to