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