Thread overview
Cannot implicitly convert expression of type const(string[]) to string[]
Jan 08, 2021
Andrey
Jan 08, 2021
kdevel
Jan 08, 2021
tsbockman
Jan 08, 2021
tsbockman
Jan 09, 2021
Ali Çehreli
Jan 09, 2021
Paul Backus
Jan 09, 2021
tsbockman
January 08, 2021
Hello,

> struct Value
> {
>     int value;
>     string data;
>     string[] text;
> }
> 
> void test(const ref Value value)
> {
>     Value other = void;
>     other.text = value.text;
> }
> 
> void main()
> {
>     Value row;
>     row.value = 10;
>     row.data = "ttttggg";
> 
>     test(row);
> }

I want to pass variable "row" inside function "test" as a read only parameter.
Inside I create another variable and try to assign field "text".
On that line I get:
> Error: cannot implicitly convert expression value.text of type const(string[]) to string[].

1. How to assign correctly (and without dup/ugly cast())?
2. Or how to pass "row" correctly?
January 08, 2021
On Friday, 8 January 2021 at 20:43:37 UTC, Andrey wrote:

[...]

> 2. Or how to pass "row" correctly?

Drop the "const" before "ref".


January 08, 2021
On Friday, 8 January 2021 at 20:43:37 UTC, Andrey wrote:
> Hello,
>
>> struct Value
>> {
>>     int value;
>>     string data;
>>     string[] text;
>> }
>> 
>> void test(const ref Value value)
>> {
>>     Value other = void;
>>     other.text = value.text;
>> }
>> 
>> void main()
>> {
>>     Value row;
>>     row.value = 10;
>>     row.data = "ttttggg";
>> 
>>     test(row);
>> }
>
> I want to pass variable "row" inside function "test" as a read only parameter.
> Inside I create another variable and try to assign field "text".
> On that line I get:
>> Error: cannot implicitly convert expression value.text of type const(string[]) to string[].
>
> 1. How to assign correctly (and without dup/ugly cast())?
> 2. Or how to pass "row" correctly?

Your code has a logical inconsistency which the compiler has rightly detected and flagged for you. You should actually fix the inconsistency, not just shut the compiler up.

If the compiler accepted your code, it would be possible to later modify the array pointed at by `row.text` through `other.text`, because `other.text` is not `const`. This would violate your stated desire to pass `row` as read-only.

There are various ways you can fix this, depending on your needs:
    1) Change the type of `other` to `const(Value)` so that `other.text` cannot be used to modify `row.text`.
    2) Change the type of `Value.text` to `const(string[])` so that it cannot be used to modify `row.text`.
    3) Assign a copy (`dup`) of `row.text` to `other.text` instead, so that mutating the array pointed to by `other.text` will not modify the original read-only `row`.
    4) Implement a copy-on-write scheme for `Value.text`.
    5) Tell the compiler that `other.text` may be used to mutate `row.text` by `const` from the `value` parameter of `test`. Do not do this unless that's really what you want!

(1) and (2) are good options, if their limitations happen to fit whatever you're really doing. (Note that either requires you to write a constructor for `Value` since you won't be able to directly write to `Value.text` anymore.)

Unless you are managing huge quantities of data with this `Value` type, (3) is a perfectly reasonable thing to do and you shouldn't feel guilty about the small "waste" of memory and CPU time for the extra allocation. (4) is an option of last resort - not a crazy one, but not worth the trouble if the amount of data is small relative to system resources and/or the other activities of the program.

(5) is simple but WRONG unless you are trying to create multiple references to the same mutable data.
January 08, 2021
On Friday, 8 January 2021 at 23:10:13 UTC, tsbockman wrote:
>     5) Tell the compiler that `other.text` may be used to mutate `row.text` by `const` from the `value` parameter of `test`. Do not do this unless that's really what you want!

Oops, that should say:

    5) Tell the compiler that `other.text` may be used to  mutate `row.text` by *removing* `const` from the `value` parameter of `test`. Do not do this unless that's really what you want!
January 08, 2021
On 1/8/21 3:10 PM, tsbockman wrote:

> On Friday, 8 January 2021 at 20:43:37 UTC, Andrey wrote:
>> Hello,
>>
>>> struct Value
>>> {
>>>     int value;
>>>     string data;
>>>     string[] text;

The destination is immutable(char)[]. The characters cannot be changed. We can still append but other slices that share the same data is protected by D's no-stomp decision.

>>> }
>>>
>>> void test(const ref Value value)
>>> {
>>>     Value other = void;
>>>     other.text = value.text;

Even though the source is 'const ref', other.text is a copy of the slice object (the pointer and the length). Because the elements are immutable, other.text cannot mutate value.text.

>>> }
>>>
>>> void main()
>>> {
>>>     Value row;
>>>     row.value = 10;
>>>     row.data = "ttttggg";
>>>
>>>     test(row);
>>> }

> Your code has a logical inconsistency

I don't see where. I think this is one of those too strict cases of D where I unhappily slap a cast and move on.

Ali

January 09, 2021
On Saturday, 9 January 2021 at 02:07:50 UTC, Ali Çehreli wrote:
> On 1/8/21 3:10 PM, tsbockman wrote:
>
> > On Friday, 8 January 2021 at 20:43:37 UTC, Andrey wrote:
> >> Hello,
> >>
> >>> struct Value
> >>> {
> >>>     int value;
> >>>     string data;
> >>>     string[] text;
>
> The destination is immutable(char)[]. The characters cannot be changed. We can still append but other slices that share the same data is protected by D's no-stomp decision.
>
> >>> }
> >>>
> >>> void test(const ref Value value)
> >>> {
> >>>     Value other = void;
> >>>     other.text = value.text;
>
> Even though the source is 'const ref', other.text is a copy of the slice object (the pointer and the length). Because the elements are immutable, other.text cannot mutate value.text.

Remember, `string[]` means `immutable(char)[][]`, so there are actually two layers of pointer + length here. The outer one is copied, but the inner one is not, which means that mutating `other.text[0]` would also mutate `value.text[0]` if this assignment were allowed.
January 09, 2021
On Saturday, 9 January 2021 at 02:07:50 UTC, Ali Çehreli wrote:
> The destination is immutable(char)[].

No, it's not. string[] means immutable(char)[][] - note the second set of brackets.

> Even though the source is 'const ref', other.text is a copy of the slice object (the pointer and the length). Because the elements are immutable, other.text cannot mutate value.text.

The elements are not immutable. Each element is an mutable slice of immutable characters.

> > Your code has a logical inconsistency
>
> I don't see where. I think this is one of those too strict cases of D where I unhappily slap a cast and move on.

The cast you propose breaks the type system. For example:

module app;

import std.stdio;

struct Value
{
    int value;
    string data;
    string[] text;
}

void test(const ref Value value) @trusted // Don't do this!
{
    Value other = void;
    other.text = cast(string[]) value.text; // This cast is @system for good reason.
    other.text[0] = "Oops";
}

void main() @safe
{
    immutable(Value) row = {
        value: 10,
        data: "ttttggg",
        text: [ "Don't change me, bro!" ]
    };

    writeln(row.text);
    test(row);
    writeln(row.text);
}