Re: how much "real-life" code can be marked @safe ?

2021-07-04 Thread Paul Backus via Digitalmars-d-learn

On Sunday, 4 July 2021 at 08:43:11 UTC, Alexandru Ermicioi wrote:

On Saturday, 3 July 2021 at 20:09:56 UTC, tsbockman wrote:
On Saturday, 3 July 2021 at 16:06:33 UTC, Alexandru Ermicioi 
wrote:
3. An edge case. Ex: You need to mutate some data and then 
assume it is immutable in a constructor.


Can you give a valid example where that is necessary? The main 
examples that I can think of either can be `@safe` with the 
right API, or are motivated by a desire to avoid the GC and/or 
druntime, thus falling under (1).


Can't remember any specific code now, but suppose you have a 
mutable object as input to a function or constructor. You need 
to return or assign an immutable copy of that struct and you 
can do that with right copy constructor on that object, but 
before that you need to do a couple of mutations on that 
object. In this use case you can't avoid cast(immutable) 
easily. Note: it is desired to not mutate the original object.


```d
immutable(Foo) example(ref Foo input)
{
Foo mutableCopy = input;
mutableCopy.mutate();
return immutable(Foo)(mutableCopy); // call copy ctor
}
```

I guess if your object is very expensive to copy you might want 
to use `cast(immutable)` here, but IMO the real solution is to 
refactor your code so that the call to `.mutate()` is not 
necessary.


For example, let's say that what `mutate` does is change the 
member variable `bar`. You could rewrite the above as:


```d
immutable(Foo) example(ref Foo input)
{
auto copy = immutable(Foo)(
// update this field
computeNewValue(input.bar),
// copy the rest
input.baz,
input.quux,
/* ... */
);
return copy;
}
```


Re: how much "real-life" code can be marked @safe ?

2021-07-04 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Saturday, 3 July 2021 at 20:09:56 UTC, tsbockman wrote:
On Saturday, 3 July 2021 at 16:06:33 UTC, Alexandru Ermicioi 
wrote:
3. An edge case. Ex: You need to mutate some data and then 
assume it is immutable in a constructor.


Can you give a valid example where that is necessary? The main 
examples that I can think of either can be `@safe` with the 
right API, or are motivated by a desire to avoid the GC and/or 
druntime, thus falling under (1).


Can't remember any specific code now, but suppose you have a 
mutable object as input to a function or constructor. You need to 
return or assign an immutable copy of that struct and you can do 
that with right copy constructor on that object, but before that 
you need to do a couple of mutations on that object. In this use 
case you can't avoid cast(immutable) easily. Note: it is desired 
to not mutate the original object.


The summary is this: the construction of immutable instances can 
be done just through initialization statement. You can't mutate 
the instance and then assign/assume it as an immutable.


4. Functionality that doesn't account for @safe/immutable or 
any other features when it can in standard library.


True, although it's just another example of my point (2). The 
standard library and druntime are dependencies, too...


Right, didn't take it that way.



The one exception here is when the array is already typed 
`inout` before it is passed to the constructor. But, that's an 
example of (2) since this logic applies transitively throughout 
the call stack: if you need to call `dup` anywhere, don't erase 
the constness with `inout`.


Yeah this is a working workaround, for this specific use case. I 
still would prefer inout version, even if I need to do some 
unsafe casts due to clear intentions it gives the user, and 
better errors.

Anyway this is just a proof of your 2nd point.




Re: how much "real-life" code can be marked @safe ?

2021-07-03 Thread tsbockman via Digitalmars-d-learn
On Saturday, 3 July 2021 at 16:06:33 UTC, Alexandru Ermicioi 
wrote:
3. An edge case. Ex: You need to mutate some data and then 
assume it is immutable in a constructor.


Can you give a valid example where that is necessary? The main 
examples that I can think of either can be `@safe` with the right 
API, or are motivated by a desire to avoid the GC and/or 
druntime, thus falling under (1).


4. Functionality that doesn't account for @safe/immutable or 
any other features when it can in standard library.


True, although it's just another example of my point (2). The 
standard library and druntime are dependencies, too...


Take for example array.dup, there is no inout alternative for 
it,
and you're pretty much stuck with trusted code, when you'd like 
to dup an array that is inout.


`inout` is usually just a convenient way to use one 
implementation to handle mutable, `const` and `immutable` cases. 
In those rare cases where `inout` itself won't work, it is almost 
always possible to accomplish the same thing using `template 
this` or separate overloads:


```D
import std.traits : Unqual, CopyConstness;

struct A {
int*[] arr;
this(this This, Arr)(Arr arr) scope pure @safe nothrow
if(is(Arr : E[], E) && is(E : CopyConstness!(This, 
Unqual!E)))

{
this.arr = arr.dup;
}
}

void main()
{
A ma = new int*[5];
const(A) ca = new const(int*[3]);
const(A) ia = new immutable(int*[4]);
}
```

Again, it's certainly not obvious how to do this, or why it is 
necessary, but it is *possible*.


The one exception here is when the array is already typed `inout` 
before it is passed to the constructor. But, that's an example of 
(2) since this logic applies transitively throughout the call 
stack: if you need to call `dup` anywhere, don't erase the 
constness with `inout`.


Re: how much "real-life" code can be marked @safe ?

2021-07-03 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Friday, 2 July 2021 at 22:08:31 UTC, tsbockman wrote:

(Responding out of order:)

On Friday, 2 July 2021 at 00:26:52 UTC, someone wrote:
But when you start attempting to declare @safe chunks of code 
that actually DO things ... well, it seems end-of-the-story.


If you find yourself unable to get real work done in `@safe` 
code, this is almost certainly a sign of one of the following 
problems:


0) You don't fully understand the purpose and valid use of any 
or all of the `@trusted`, `inout`, `scope`, and `return` 
annotations.


1) Your code is avoiding use of the garbage collector, and/or 
does not have `-dip1000` enabled. (`@safe` is still quite 
useful without the garbage collector, but even with `-dip1000` 
you'll still need a lot of `@trusted` code.)


2) You have at least one dependency that isn't correctly 
designed for use with `@safe`.


I'd add:
3. An edge case. Ex: You need to mutate some data and then assume 
it is immutable in a constructor.


4. Functionality that doesn't account for @safe/immutable or any 
other features when it can in standard library. Take for example 
array.dup, there is no inout alternative for it, and you're 
pretty much stuck with trusted code, when you'd like to dup an 
array that is inout.

manual.

Although these two should be on the lowest place in this list by 
priority.





Re: how much "real-life" code can be marked @safe ?

2021-07-02 Thread tsbockman via Digitalmars-d-learn

(Responding out of order:)

On Friday, 2 July 2021 at 00:26:52 UTC, someone wrote:
But when you start attempting to declare @safe chunks of code 
that actually DO things ... well, it seems end-of-the-story.


If you find yourself unable to get real work done in `@safe` 
code, this is almost certainly a sign of one of the following 
problems:


0) You don't fully understand the purpose and valid use of any or 
all of the `@trusted`, `inout`, `scope`, and `return` annotations.


1) Your code is avoiding use of the garbage collector, and/or 
does not have `-dip1000` enabled. (`@safe` is still quite useful 
without the garbage collector, but even with `-dip1000` you'll 
still need a lot of `@trusted` code.)


2) You have at least one dependency that isn't correctly designed 
for use with `@safe`.


As long as you're willing to use the garbage collector, almost 
all algorithms can be expressed in an efficient `@safe` way, 
**but** this sometimes requires knowledge of several advanced 
features of D, and how and when to combine them.



but no castings are allowed with @safe


That is simply not true. Many explicit casts are `@safe`, as are 
nearly all implicit casts.


Casting away `const` or `immutable` is `@system`, but should 
hardly ever be necessary if you understand how to write 
constructors correctly (see below), and use `inout` appropriately.


Many reinterpret casts are also illegal, but `union` and `class` 
provide `@safe` ways of achieving the same goals for the common 
cases.


- almost all this() constructors are a no-go providing you do 
something in-between with the parameters until you assign them 
back to the target variables;


Constructors can be `@safe`, but you have to understand how they 
work in D:


The key difficulty is that what appears to be the first 
"assignment" to each field in a constructor actually constructs 
that field, instead. Subsequent assignments really are 
assignments.


So, when constructing a `const` object each field can only be 
"assigned" once, because the fields are also `const`, even in the 
object's constructor. If you need to do complex calculations to 
determine field values, use temporary variables and `static` 
helper functions until you get the final value, and then 
unconditionally assign that value to a field *once*.


**TLDR**; You probably don't understand how to use `@safe` 
correctly. (Most people don't; the rules are complicated and 
non-obvious.)


Post some example code that you think can't be `@safe`, and I can 
probably show you how to fix it, unless it involves manual memory 
management or an incompatible dependency. Even then, the 
non-`@safe` code can often be isolated behind an `@trusted` API.


Re: how much "real-life" code can be marked @safe ?

2021-07-02 Thread Steven Schveighoffer via Digitalmars-d-learn

On 7/1/21 8:26 PM, someone wrote:

... just wondering:

I am writing pretty trivial code, nothing out of the ordinary, and 
attempted to check how much of it could be marked safe ...


It should be quite a bit.



- Lots of tiny common library functions are pretty easy

- Getter/Setter properties are easy too

- almost all this() constructors are a no-go providing you do something 
in-between with the parameters until you assign them back to the target 
variables; eg: you have a char parameter (that you need to do something 
with it) that needs to be assigned to a class/structure string member 
variable and then it needs a cast ... but no castings are allowed with 
@safe


This doesn't make a lot of sense, lots of constructors are safe.

And casting is OK as long as it's safe, including class downcasts.



But when you start attempting to declare @safe chunks of code that 
actually DO things ... well, it seems end-of-the-story.


This is why you encapsulate the unsafe parts in @trusted functions.

The classic example is the posix `read` and `write` functions. The 
function `int read(int fd, void *data, int len)` is not safe, nor can it 
be trusted. But a trusted wrapper is possible: `@trusted int 
readSafe(int fd, ubyte[] data)`. Now all of a sudden, safe functions 
have more utility.




Declaring @safe void() main() {...} as I was advised in some previous 
post (to avoid declaring @safe everywhere) is almost impossible unless 
you are doing the hello world app.


The point is to mark main @safe, and then fix whatever things that 
aren't safe that it calls to provide a safe interface.




I would love to hear how you, I mean the community, approach code 
safeness ?


Any function I want to make safe, I mark it as @safe. Then I fix the 
compiler complaints until it compiles (including functions that it 
calls), using as little @trusted code as possible.


-Steve


Re: how much "real-life" code can be marked @safe ?

2021-07-02 Thread Alexandru Ermicioi via Digitalmars-d-learn

On Friday, 2 July 2021 at 00:26:52 UTC, someone wrote:

... just wondering:
...


Imho, if you want all of the app to be safe, and you cannot avoid 
unsafe code, then there are two choices:
1. Mark the method doing unsafe stuff as @trusted, or pieces of 
code which are unsafe with trusted lambda hack.

2. Mark the top caller of your @system methods as @trusted.

In both cases, unsafe code should be manually checked as best as 
it can be done. Best is to think twice whether you can redesign 
your code to avoid unsafe operations.


On PR review step if there is such thing for your app, reviewers 
should carefully review those @trusted blocks of code.


Oh well, and tests, tests and more tests, i.e. It should be 
thoroughly tested.


Best regards,
Alexandru.


how much "real-life" code can be marked @safe ?

2021-07-01 Thread someone via Digitalmars-d-learn

... just wondering:

I am writing pretty trivial code, nothing out of the ordinary, 
and attempted to check how much of it could be marked safe ...


- Lots of tiny common library functions are pretty easy

- Getter/Setter properties are easy too

- almost all this() constructors are a no-go providing you do 
something in-between with the parameters until you assign them 
back to the target variables; eg: you have a char parameter (that 
you need to do something with it) that needs to be assigned to a 
class/structure string member variable and then it needs a cast 
... but no castings are allowed with @safe


But when you start attempting to declare @safe chunks of code 
that actually DO things ... well, it seems end-of-the-story.


Declaring @safe void() main() {...} as I was advised in some 
previous post (to avoid declaring @safe everywhere) is almost 
impossible unless you are doing the hello world app.


I would love to hear how you, I mean the community, approach code 
safeness ?