mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 11:07:39 +03:00
88 lines
3.9 KiB
Text
88 lines
3.9 KiB
Text
isSomeString and isNarrowString are now `false` for enums
|
|
|
|
Previously, enums whose base type was a string type were `true` for
|
|
$(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits).
|
|
Occasionally, this was useful, but in general, it was a source of bugs, because
|
|
code that works with strings does not necessarily work with enums whose base
|
|
type is a string, making it easy to write code where an enum would pass the
|
|
template constraint and then the template would fail to compile. For instance,
|
|
enums of base type string are `false` for
|
|
$(REF isInputRange, std, range, primitives). As such, it really doesn't make
|
|
sense for $(REF isSomeString, std, traits) and
|
|
$(REF isNarrowString, std, traits) to be `true` for enums.
|
|
|
|
For some code, this will be a breaking change, but most code will either be
|
|
unaffected, or it will then fail with enums at the template constraint instead
|
|
of inside the function. So, the risk of code breakage is minimal but does exist.
|
|
Other code will now be able to remove stuff like `!is(T == enum)` from its
|
|
template constraints, since that is now part of
|
|
$(REF isSomeString, std, traits) and $(REF isNarrowString, std, traits), but
|
|
it will continue to compile with the now unnecessary check for enums.
|
|
|
|
Code that uses $(REF isSomeString, std, traits) or
|
|
$(REF isNarrowString, std, traits) in a template constraint and has no other
|
|
conditions which would prevent enums from passing the constraint but then would
|
|
fail to compile inside the template if an enum were passed to it will now fail
|
|
to compile at the template constraint instead of inside the template. So, such
|
|
code is fixed rather than broken.
|
|
|
|
The rare code that does break because of these changes is code that uses
|
|
$(REF isSomeString, std, traits) or $(REF isNarrowString, std, traits) in a
|
|
template constraint or static if and does not use other conditions to prevent enums
|
|
from passing and actually has code in the template which compiles with both strings
|
|
and enums with a base type of string. Such code will need to be changed to use
|
|
$(REF OriginalType, std, traits), $(REF asOriginalType, std, conv), or
|
|
$(REF StringTypeOf, std, traits) instead. e.g. for enums to pass the constraint,
|
|
instead of
|
|
|
|
---
|
|
auto foo(S)(S str)
|
|
if (isSomeString!S)
|
|
{
|
|
...
|
|
}
|
|
---
|
|
|
|
the code would be
|
|
|
|
---
|
|
auto foo(S)(S str)
|
|
if (isSomeString!(OriginalType!S))
|
|
{
|
|
...
|
|
}
|
|
---
|
|
|
|
As a rule of thumb, generic code should either disallow implicit conversions
|
|
and force the caller to do the conversion explicitly (which generally is the
|
|
least error-prone approach), or it should force the conversion to the desired
|
|
type before the parameter is used in the function so that the function is
|
|
definitely operating on the desired type and not just on one that implicitly
|
|
converts to it and thus will work regardless of whether the argument was the
|
|
exact type or implicitly converted to it.
|
|
|
|
However, great care should be taken if the implicit conversion will result in
|
|
slicing the parameter or otherwise referring to its address, since that makes
|
|
it very easy for a reference to local memory to escape the function, whereas if
|
|
the conversion is done at the call site, then the slicing is done at the call
|
|
site, so the dynamic array is still valid when the function returns.
|
|
Fortunately, enums with a base type of string do not have that problem, but
|
|
other types which implicitly convert to dynamic arrays (such as static arrays)
|
|
do have that problem, which is a big reason why it's generally recommended to
|
|
not have generic functions accept types based on implicit conversions.
|
|
|
|
It is recommended that if a function is supposed to accept both strings and
|
|
types which implicitly convert to a string type, then it should explicitly
|
|
accept dynamic arrays but be templated on the element type. e.g.
|
|
|
|
---
|
|
auto foo(C)(C[] str)
|
|
if (isSomeChar!C)
|
|
{
|
|
...
|
|
}
|
|
---
|
|
|
|
That way, any implicit conversions are done at the call site and do not
|
|
introduce @safety problems inside the function. It also tends to result in
|
|
template constraints which are shorter and more easily understood.
|