| |
 | Posted by Jonathan M Davis in reply to Andy Valencia | Permalink Reply |
|
Jonathan M Davis 
Posted in reply to Andy Valencia
| On Wednesday, May 7, 2025 10:51:27 PM Mountain Daylight Time Andy Valencia via Digitalmars-d-learn wrote:
> On Thursday, 8 May 2025 at 00:53:20 UTC, Mike Parker wrote:
> >> tst44.d(6): Error: cannot implicitly convert expression
> >> `fromStringz(ctime(null))` of type `char[]` to `string`
> > `fromStringz` is giving you a slice of a `char*`, typed `char[]`.
> >
> > `string` is `immutable(char)[]`, so you can't assign `char[]`
> > to it.
> >
> > You could:
> >
> > * change the type of `s` to `char[]`
> > * call `.idup` on the array returned from `fromStringz` to
> > allocate a `string`
> > * use `std.conv.to`
>
> Thank you. I want to work in strings, so the first one's not an option. But both the second and third do the trick. Would you say the to!string would be the most idiomatic? It worked as "to!string(ctime(&t))", but is it safe to assume it's reliably dealing with null-terminated strings? It's the pithiest if so.
to!string definitely deals with null-terminated strings, or it wouldn't work at all. It's not the kind of thing that would work by accident. So, the only question is whether it allocates a new string or not, since that could matter depending on what kind of memory the pointer points to (e.g. you don't want a slice of malloc-ed memory outliving when it would be freed).
If the mutability of the argument was compatible with that of the requested type (e.g. converting char* to char[] or char* to const(char)[]), then std.conv.to could just slice the pointer from the start of the string up to the null character (which is what fromStringz does), so whether you got a newly allocated array or not would depend on the current implementation, and relying on it allocating or not arguably wouldn't be wise.
However, what you're doing is converting a char* to string, and you can't implicitly convert char* to immutable char*, so it's not possible to slice a char* and get a string in @safe code. This means that if you pass a char* (or a const(char)*) to to!string, you're guaranteed to get a newly allocated string.
Now, fromStringz(ptr).idup is guaranteed to allocate even if ptr is
immutable(char)* or immutable(char*), so if there were any concern that you
might ever have an immutable(char)* and wanted to guarantee that you were
going to allocate a new array, then using fromStringz(ptr).idup would be
better, but realistically, you're not going to get immutable(char)* unless
you got a ptr from a string, and those are almost always allocated by D's GC
(in which case, slicing would be fine). You're not going to get anything
that's immutable from C code.
And it could be argued that fromStringz(ptr).idup is more idiomatic, because it is clearly allocating without having to even think about whether the implementation could be slicing instead of allocating a copy.
But ultimately, whether you use to!string(ptr) or fromStringz(ptr).idup is really a matter of personal preference - especially if you're not dealing with generic code. I suspect that more folks would think that using to!string(ptr) looked better, but I don't know. Personally, I'd probably use fromStringz(ptr).idup just to make the operation explicit, but that's simply my preference. Do whichever you prefer. They both work just fine.
- Jonathan M Davis
|