Trying to alias this a grapheme range + making it a forward range

ag0aep6g anonymous at example.com
Mon Jul 8 23:01:49 UTC 2019


On 08.07.19 23:55, aliak wrote:
> struct ustring {
>      string data;
>      this(string data) {
>          this.data = data;
>      }
>      auto get() {
>          static struct Range {
>              typeof(string.init.byGrapheme) source;
>              bool empty() { return source.empty; }
>              void popFront() { source.popFront; }
>              auto front() { return source.front[]; }
>              auto save() { return this; };
>          }
>          return Range(this.data.byGrapheme);
>      }
>      alias get this;
> }
> 
> But I keep on ending up with a UTFException: "Encoding an invalid code 
> point in UTF-8" with code like:
> 
> writeln("hello".ustring);

`source.front` is a temporary `Grapheme` and you're calling `opSlice` on 
it. The documentation for `Grapheme.opSlice` warns: "Invalidates when 
this Grapheme leaves the scope, attempts to use it then would lead to 
memory corruption." [1]

So you can't return `source.front[]` from your `front`. You'll have to 
store the current `front` in your struct, I guess.

Also, returning a fresh range on every `alias this` call is asking for 
trouble. This is an infinite loop:

     auto u = "hello".ustring;
     while (!u.empty) u.popFront();

because `u.empty` and `u.popFront` are called on fresh, non-empty, 
independent ranges.

> Problem 2:
> 
> How can I get the aliased ustring type to behave as a ForwardRange? If I 
> add the save method to the voldermort range type, the 
> isForwardRange!ustring fails because the requirement on isForwardRange 
> checks to see if save returns the same type it's called on. Which is not 
> the case here since typeof(ustring.save) == ustring.get.Range). But 
> nontheless does have a save method.

You must provide a `save` that returns a `ustring`. There's no way 
around it.

Maybe make `ustring` itself the range. In the code you've shown, the 
`alias this` only seems to make everything more complicated. But you 
might have good reasons for it, of course.

By the way, your're not calling `source.save` in `Range.save`. You're 
just copying `source`. I don't know if that's effectively the same, and 
even if it is, I'd advise to call `.save` explicitly. Better safe than 
sorry.


[1] https://dlang.org/phobos/std_uni.html#.Grapheme.opSlice


More information about the Digitalmars-d-learn mailing list