https://issues.dlang.org/show_bug.cgi?id=14125
--- Comment #66 from Dicebot <pub...@dicebot.lv> --- Thanks, Walter! This comment alone was more useful and meaningful than rest of this thread combined :) Let me use example from the very same std.file to oppose: S readText(S = string)(in char[] name) @safe if (isSomeString!S) { import std.utf : validate; static auto trustedCast(void[] buf) @trusted { return cast(S)buf; } auto result = trustedCast(read(name)); validate(result); return result; } There are two important bits here: 1) This is verified to be safe only if call to `validate` is safe. Making it all @trusted would allow to change `validate` to @system and silently break the promise. 2) Cast is restricted to string types. Pretty much only way it can go wrong is if function signature changes to allow other types - it is effectively @safe function that compiler fails to identify as one. Wrapper is marked @trusted incorrectly but that is intentional. @trusted here is not used to tell function can actually be trusted but to keep everything else plain @safe. Alternative solution is to invert it: S readText(S = string)(in char[] name) @trusted if (isSomeString!S) { () @safe { import std.utf : validate; auto s = read(name); } (); auto result = cast(S) s; () @safe { validate(result); } (); return result; } Which also solves same problem but it result in creation of much more wrappers and makes reading the source even harder (even despite the fact I cheated here and used lambdas instead of nested functions). --