How to get rid of const / immutable poisoning (and I didn't even want to use them...)
ag0aep6g via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Mon May 22 14:10:55 PDT 2017
On 05/22/2017 10:13 PM, Enjoys Math wrote:
> immutable struct String(T) {
Marking a whole struct as `immutable` is usually a mistake. It applies
to all methods, making them unusable on mutable and const instances.
> public:
> this(immutable T[] s) {
This means you can create `String`s only with immutable arrays. Again,
not what you want usually. You can use `inout` to make a constructor
that accepts mutable, const, and immutable arrays when constructing
mutable, const, and immutable instances, respectively:
----
this(inout T[] s) inout { ... }
----
If `inout` doesn't work for some reason, you will have to define
distinct constructors:
----
this(T[] s) { ... }
this(const T[] s) const { ... }
this(immutable T[] s) immutable { ... }
----
> this.s = s;
> }
>
> alias s this;
>
> string toString() const { return to!string(s); }
> string flattened() const { string t = ""; foreach (c; s) t ~=
> to!string(c); return t; }
>
> size_t toHash() const @system pure nothrow
> {
> return hashOf(flattened());
> }
>
> bool opEquals(ref const String t) const @system pure nothrow
> {
> if (t.length != this.length)
> return false;
>
> for(size_t k=0; k < t.length; k++)
Off topic: foreach (k; 0 .. t.length)
> if (t.s[k] != this.s[k]) return false;
Seeing how `k` is used, could also make it:
----
foreach (k, e; t)
if (e != this.s[k]) return false;
----
> return true;
> }
>
> // A compressible is a substring that occurs (non-overlappingly)
> >=2 and has a length >= 3
> // or one that occurs >= 3 and has a length of 2
> String[] compressibles() const {
> auto count = compressibleCount();
> String[] substrings;
> foreach (s, n; count)
> substrings ~= s;
> return substrings;
> }
>
> size_t[String] compressibleCount() const {
> auto count = substringCount(2, size_t(s.length / 2));
Off topic: substringCount's second parameter is an int, not a size_t.
Converting from size_t to int like this works with -m32, but if large
values are possible, you've got a bug. With -m64 it simply doesn't work.
You can use `to!int` which throws on failure.
> foreach (str, n; count)
> if (str.length == 2 && n < 3 || str.length >= 3 && n < 2)
> count.remove(str);
>
> return count;
> }
>
> String[] substrings(int minLen=0, int maxLen=-1) const {
> auto count = substringCount();
> String[] substrings;
> foreach (s, n; count)
> substrings ~= s;
> return substrings;
> }
>
>
> size_t[String] substringCount(int minLen=0, int maxLen=-1) const {
> if (maxLen == -1)
> maxLen = s.length;
Same bug-prone conversion from size_t to int as above.
> assert (maxLen <= s.length && minLen >=0 && minLen <= maxLen);
>
> size_t[String] count;
>
> if (minLen == 0)
> count[empty] = s.length + 1;
`empty` is not a String, it's a T[]. Works when you change `empty` to
`static empty = String([]);`.
> for (size_t i=0; i < s.length - minLen; i++) {
>
> size_t max_len = min(s.length - i, maxLen);
>
> for (size_t l=1; l < max_len; l++) {
> auto substr = String(this.s[i..i+l]);
We're in a const method, so `this.s[i..i+l]` is const as well. If you
want to return a result with mutable keys, you have to `.dup` that slice
of s.
Alternatively, drop `const` from the method, or return a result with
const keys. Though returning `const` has similar repercussions as
returning immutable keys (next paragraphs).
Arguably, keys of associative arrays should be immutable. When the keys
change and the associative isn't re-hashed, it's going to behave
erratically.
Making the keys immutable would mean you have to `.idup` the slices.
You'd also have `.dup` in `compressibles` and `substring` if they're
supposed to return mutable `String`s.
> if (!(substr in count))
> count[substr] = 0;
> else
> count[substr] ++;
> }
> }
>
> return count;
> }
>
> public:
> immutable(T)[] empty = [];
>
> private
> T[] s;
> }
More information about the Digitalmars-d-learn
mailing list