Preferred Way of Building Strings in D

141 views Asked by At

What is the preferred way of constructing strings with regards to the function attributes @safe, pure and nothrow and compile-time and run-time performance of the parenting function?

Should we either use, for instance

format("Variable x=%s should equal %s", x, x_)

or

"Variable x=" ~to!string(x)~ " should equal " ~to!string(x_)

I reckon the format version is easier on the eye but is it better in other regards?

There could be a slight performance hit on compilation time because the format strings and its arguments has to be CTFEed right?

2

There are 2 answers

1
Adam D. Ruppe On BEST ANSWER

The first version is generally better. There's no significant difference in compiliation performance (if any, forrmat isn't ctfe), and format() should actually perform better.

The binary "foo" ~ "bar" is often surprisingly expensive because it allocates intermediate results that the garbage collector then has to clean up.

On the dmd version I have, neither version actually compiles as safe, nothrow, pure. I think they fixed this in phobos git, but I'm not sure. Regardless, right now neither actually works, and there's no easy workaround aside from implementing your own function, unless it is just for debugging.

The pure requirement is relaxed in debug statements, and you can wrap non-@safe functions in @system, and throwing functions in try/catch to get those attributes in.

Thus, this would actually compile:

// trusted gets around safe
@trusted pure nothrow string myformat(T...)(in string fmt, in T t) {
import std.string;
    // debug gets around the pure requirement
debug try // try gets around nothrow
    return format(fmt, t);
catch(Exception e) { }

return null;
}

@safe nothrow pure void main() {
import std.conv;
string s = myformat("test %s", 10);
    assert(0, s); // the assert message shows up
}

compile with the -debug switch: dmd test.d -debug

So not a great solution, but until the phobos functions with the proper attributes are released, or if you want to write your own format() or to() functions (not really that hard, you can do int to string in < 10 lines) probably the best you can do.

0
V-R On

As the accepted answer says, prefer format!, and prefer to pass the format-string as a template argument:

#!/usr/bin/env rdmd

module test;

import std.format; // or std.string, which publicly imports std.format
import std.stdio;

void main()
{
    writeln(format("%s%s","run-time error 'Orphan format specifier'"));
    writeln(format!"%s%s"("compile-time error 'Orphan format specifier'"));
}

It is available since DMD 2.074.0 released in April 2017.