Sometimes it becomes clear that a feature is just a bad idea. These are called deprecated features and once they are judged to merit removal from the language, they follow a procedure in order to allow plenty of time for users to adjust to the change.
Throwing exceptions from the contracts of a nothrow function was permitted:
float sqrt(float n) nothrow in { if (n < 0) throw new Exception("n must be positive"); } do { }Corrective Action
Remove the nothrow attribute or rewrite the contract using assertions instead.
float sqrt(float n) nothrow in { assert(n >= 0); } do { }Rationale
Since a function's preconditions and postconditions are implicitly executed before and after the function's body, allowing them to throw would break the guarantees of the nothrow attribute.
Class allocators and deallocatorsD classes can have members customizing the (de)allocation strategy.
class Foo { new(uint size, ...) { return malloc(size); } delete(void* obj) { free(obj); } } Foo foo = new(...) Foo(); delete foo;Corrective Action
Move the (de)allocation strategy out of the class
class Foo { } T make(T, Args...)(auto ref Args args) if (is(T == Foo)) { enum size = __traits(classInstanceSize, T); void* mem = malloc(size); scope (failure) free(mem); return mem !is null ? emplace!T(mem[0..size], args) : null; } void dispose(T)(T obj) { auto mem = cast(void*) obj; scope (exit) free(mem); destroy(obj); } Foo foo = make!Foo(); if (foo !is null) dispose(foo);Rationale
Classes should not be responsible for their own (de)allocation strategy.
Implicit comparison of different enumsComparison of different enumerated type was allowed:
enum Status { good, bad } enum OtherStatus { ok, no } static assert(Status.good == OtherStatus.ok);Corrective Action
Comparison between unrelated enumerated types should be done with std.conv.asOriginalType
import std.conv : asOriginalType; assert(Foo.x.asOriginalType == Bar.y.asOriginalType);Rationale
Code correctness is improved by disallowing comparison of unrelated enumerated types. Implicit comparison of different enum types often resulted in hard to spot bugs.
enum { X } enum { Y } void main() { auto b = X == Y; assert(b); }Implicit string concatenation
Two adjacent strings were implicitly concatenated:
string foo = "Hello" "World";
This feature was handy for a long string that spans multiple lines, however, it is possible to get the same behaviour explicitly by using the concatenation operator ('~'):
string foo = "Hello" ~ "World";Corrective Action
Replace implicit string concatenation by explicit one, using '~'.
RationaleThis is a very early feature of the language, which is nowadays totally covered by the concatenation operator: it is performed at compile time for constants and doesn't result in memory allocation.
However, having implicit concatenation can and did result in hard to spot bugs, for example:
string[] names = [ "Anna", "Michael" "Emma", "David" ];Using the result of a comma expression
The comma operator (,) allows executing multiple expressions and discards the result of them except for the last which is returned.
int a = 1; int b = 2; bool ret = a == 2, b == 2;
It's also common to use the comma operator in for-loop increment statements to allow multiple expressions.
for (; !a.empty && !b.empty; a.popFront, b.popFront)Corrective Action
If possible, split the comma operator in two statements. Otherwise use lambdas.
auto result = foo(), bar(); foo(); auto result = bar(); auto result = {foo(); return bar();}();Rationale
The comma operator leads to unintended behavior (see below for a selection) Moreover it is not commonly used and it blocks the ability to implement tuples. A selection of problems through the accidental use of the comma operator:
writeln( 6, mixin("7,8"), 9 ); template vec(T...)(T args) { ... } vec v = (0, 0, 3); int b = 2; if (a == 1, b == 2) { } void foo(int x) {} foo((++a, b)); synchronized (lockA, lockB) {}delete
Memory allocated on the GC heap can be freed with delete.
auto a = new Class(); delete a;Corrective Action
Use object.destroy() to finalize the object instead.
auto a = new Class(); destroy(a);
Note that destroy does not free the allocated memory. If necessary, call core.GC.free also.
Rationaledelete makes assumptions about the type of garbage collector available that limits which implementations can be used, and can be replaced by a library solution.
scope as a type constraintThe scope keyword can be added to a class declaration to force all instances of the class to be attributed with the scope storage class.
scope class C { } void main() { C c1 = new C(); scope C c2 = new C(); }Corrective Action
There is no current counterpart in the D programming language or library that places such a constraint on a type requiring all instances of the type to be attributed with the
scopestorage class.
Rationalescope as a type constraint was a quirk in the language without a compelling use case.
Note that this deprecation only affects the usage of scope as a type constraint attributed to a class declaration. scope as a storage class attributed to variables, function parameters, etc. is not deprecated.
Imaginary and complex typesD currently supports imaginary and complex versions of all floating point types.
float a = 2; ifloat b = 4i; cfloat c = a + b; assert(c == 2 + 4i);Corrective Action
Use the library types in std.complex.
RationaleThese types are too specialized to be a part of the core language.
128-bit integer typesD currently reserves the cent and ucent keywords for future use as 128-bit integral types. Using them will result in a compile-time error.
cent a = 18446744073709551616L; ucent b = 36893488147419103232UL;Corrective Action
Use the library types in std.int128 or core.int128.
RationaleThese types are too specialized to be a part of the core language.
Implicit catch statementOne can catch everything by using catch without specifying a type.
int[] arr = new int[](10); try { arr[42]++; } catch { writeln("An error was caught and ignored"); }Corrective Action
Either don't catch Throwable or replace catch {} with catch (Throwable) {}
int[] arr = new int[](10); try { arr[42]++; } catch (Throwable) { writeln("An error was caught and ignored"); }Rationale
Catching Throwable should not be encouraged by the language, because certain core guarantee cannot be satisfied, e.g. the stack might not get cleaned up and destructors might not get run. This change helps ensure catching Throwable is always a conscious and visible decision on the programmer's side.
.sort and .reverse properties for arraysD arrays can be manipulated using these built-in properties.
int[] x = [2, 3, 1]; assert(x.sort == [1, 2, 3]);Corrective Action
Use the generic functions in std.algorithm.
RationaleThese operations are better implemented in the standard library.
C-style array pointersC-style array pointers can be used in D.
alias float *arrayptr[10][15];Corrective Action
Replace with D-style array pointers.
alias float[15][10]* arrayptr;Rationale
The D syntax is much cleaner and easier to use.
Floating point NCEG operatorsD currently supports the NCEG floating point operators (!<>=, <>, <>=, !>, !>=, !<, !<=, !<>) for comparisons involving NaNs.
Corrective ActionUse the normal operators and std.math.traits.isNaN.
RationaleThese operators are too specialized to be a part of the core language.
clearCall an object's destructor.
auto a = new Class(); clear(a);Corrective Action
Use object.destroy() instead.
auto a = new Class(); destroy(a);Rationale
Due to Uniform Function Call Syntax (UFCS), clear can cause confusion with other methods of the same name, such as a clear method used to remove the contents of a container.
.min property for floating point typesFloating point types have the .min property to access the smallest value.
enum m = real.min;Corrective Action
Replace with .min_normal
enum m = real.min_normal;Rationale
The name min_normal is more accurate, as .min does not include denormalized floating point values.
Cast T[] to integral typeAt some point in time you could do:
ulong u = cast(ulong)[1,2];
To get the length of the array.
Corrective ActionUse the .length property instead.
RationaleUsing a cast to get the length of an array is just confusing.
Base Class ProtectionBase class protections are things like:
class A : protected B { ... }Corrective Action
Delete the protection attribute keyword from in front of the base class and base interfaces.
RationaleWith D's module system, it doesn't seem to serve any useful purpose, and has never worked correctly.
Windows 3.x and Windows 9x supportThere is some code in Phobos for Windows 3.x/9x support.
Corrective ActionUpgrade Windows or switch to another supported OS.
RationaleSupporting such outdated and rarely used OS-es isn't worth the trouble.
typedeftypedef can be used to construct a strongly-typed alias of a type.
typedef int myint; static assert(!is(myint == int));Corrective Action
Replace use of typedef with alias or use std.typecons.Typedef.
Rationaletypedef is not flexible enough to cover all use cases. This is better done with a library solution.
Using * to dereference arraysD array variables can be dereferenced to get the first element, much like pointers.
int[] arr = [1, 2, 3]; assert(*arr == 1);Corrective Action
Use indexing syntax to access first member.
int[] arr = [1, 2, 3]; assert(arr[0] == 1);Rationale
D arrays are not pointers.
invariant as an alias for immutableThe invariant storage class and type modifier is an alias for immutable.
static assert(is(invariant(int) == immutable(int)));Corrective Action
Replace all uses of invariant as a storage class or type modifier with immutable. The invariant() syntax for struct and class invariants is still supported.
RationaleThe alias is unnecessary.
Non-final switch statements without a default caseSwitch statements can be declared without a default case, and the compiler automatically adds one.
switch(a) { case 1: break; case 2: break; }Corrective Action
Add the default case manually.
switch(a) { case 1: break; case 2: break; default: assert(0); }Rationale
Missing default cases can hide bugs, and making the default case explicit should be mandatory.
Hiding base class functionsThis occurs when declaring a function in a derived class that can be called with the same arguments as a function in a base class, without overriding that function. The base class function gets hidden:
class A { void fun(int x) {} } class B : A { void fun(long x) {} }Corrective Action
Add the function to the base class, or use an alias to bring the base class overload into the derived class:
class A { void fun(int x) {} void fun(long x) {} } class B : A { void fun(long x) {} alias A.fun fun; }Rationale
This is an error that is already detected at runtime, and is being extended to compile time.
Octal literalsOctal literals can be used to enter literals in base 8.
Corrective ActionUse the std.conv.octal template.
auto x = octal!123;Rationale
The use of a leading zero is confusing, as 0123 != 123.
C-style function pointersC-style function pointers can be used in D.
alias void(*fptr)(int, long);Corrective Action
Replace with D-style function pointers.
alias void function(int, long) fptr;Rationale
The D syntax is much cleaner and easier to use.
Using length in index expressionsWhen used inside an indexing or slicing expression, length is rewritten to be the length of the array being sliced.
auto a = new int[5]; a = a[0..length-1];Corrective Action
Replace length with the equivalent '$'
RationaleThe implicitly defined length variable shadows existing declarations, and is less concise than the alternative.
Escape string literalsEscape string literals can be used to describe characters using escape sequences.
Corrective ActionPut escape sequences inside a regular string literal.
string x = "hello\n";Rationale
Escape string literals are unintuitive and unnecessary.
volatilevolatile can be used to mark statement, in order to prevent some compiler optimizations.
volatile { ... do something involving ghared variables ... }Corrective Action
Convert the code to use synchronized statements instead.
Rationalevolatile statements are a misfeature.
HTML source filesThe D compiler can parse html files by ignoring everything not contained in <code></code> tags.
<html> <code> ... source ... </code> </html>Corrective Action
Extract code to regular source files.
RationaleThis has been replaced for documentation by the introduction of ddoc
Overriding without overrideVirtual functions can currently override a function in a base class without the 'override' attribute.
class A { void fun() {} } class B : A { void fun() {} }Corrective Action
Mark overriding functions with override
class A { void fun() {} } class B : A { override void fun() {} }Rationale
Making the override attribute mandatory makes it explicit, and can catch errors when a base class function is accidentally overridden.
Lower case 'l' suffix for integer literalsLower case 'l' is an alternative suffix to denote 64 bit integer literals.
Corrective ActionUse the upper case 'L' suffix.
auto x = 123L;Rationale
The lower case suffix is easily confused with the digit '1'.
NoteIn lexical analysis phase, compiler can recognize lower case suffix 'l' to report better error message - for the use case such as C-to-D code translation. Thus DMD would continue to parse 'l' suffix.
Variable shadowing inside functionsVariable shadowing is when a variable in an inner scope has the same name as a variable in an enclosing scope.
void myFun() { int var; if (x) { int var; var = 3; } }Corrective Action
Rename shadowing variables so they don't conflict.
RationaleVariable shadowing can introduce hard to find bugs where the wrong variable is modified.
Upper case 'I' suffix for imaginary literalsThe 'I' suffix can be used to denote imaginary floating point values.
Corrective ActionUse the lower case 'i' suffix.
auto x = 1.234i;Rationale
The 'I' suffix is easily confused with the digit '1'.
if (v; e)This syntax can be used to declare a variable in an if statement condition.
if (v; calculateAndReturnPointer()) { ... }Corrective Action
Replace with an auto declaration.
if (auto v = calculateAndReturnPointer()) { ... }Rationale
The syntax is clearer with auto.
Removing an item from an associative array with deletedelete can be used to remove an item from an associative array.
int[int] aa = [1 : 2]; delete aa[1]; assert(1 !in aa);Corrective Action
Use .remove instead.
int[int] aa = [1 : 2]; aa.remove(1); assert(1 !in aa);Rationale
The delete syntax is confusing and conflicts with the normal delete syntax.
.offset propertyThe .offset property can be used to get member offset information.
struct S { int a, b; } static assert(S.b.offset == 4);Corrective Action
Use .offsetof instead
struct S { int a, b; } static assert(S.b.offsetof == 4);Rationale
The .offset syntax has been superseded by .offsetof
.size propertyThe .size property can be used to get type size information
struct S { int a, b; } static assert(S.size == 8);Corrective Action
Use .sizeof instead
struct S { int a, b; } static assert(S.sizeof == 8);Rationale
The .size syntax has been superseded by .sizeof
.typeinfo propertyThe .typeinfo property can be used to get the associated TypeInfo class.
T.typeinfoCorrective Action
Use typeid() instead
typeid(T)Rationale
The .typeinfo syntax has been superseded by typeid()
unannotated asm blocksasm blocks don't affect the function annotations.
void foo() @safe { asm { noop; } }Corrective Action
Annotate the asm blocks instead
void foo() @safe { asm @trusted { noop; } }Rationale
asm blocks may throw, contain unsafe, impure code or call the GC interfaces.
Throwing qualified objectsPreviously, an immutable, const, inout or shared exception could be thrown and then caught in an unqualified catch (Exception e) clause. That breaks type safety. Throwing a qualified object is now deprecated. This helps to prevent possible mutation of an immutable object in a catch clause.
The runtime also modifies a thrown object (e.g. to contain a stack trace) which can violate const or immutable objects. Throwing qualified objects has been deprecated for this reason also.
It is unsafe to catch an exception as immutable, inout or shared. This is because the exception may still be accessible through another mutable or non-shared reference.
auto e = new Exception("first"); try { throw e; } catch(immutable Exception ie) { e.msg = "second"; assert(ie.msg == "first"); }
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4