如何在D语言的@safe函数中进行I/O操作?

8

我正在学习D编程语言,并尽可能地限制自己使用其中的SafeD子集。然而,我发现像writeln这样的I/O函数都是@system的。如何在SafeD中进行I/O操作呢?

我正在Fedora 19 x86-64上使用从Fedora软件包下载的LDC2。

2个回答

10

你不能直接使用,或者至少不是直接使用。I/O 需要进行系统调用,这意味着需要使用 C 函数,而 C 函数不会被 @safe 修饰。由于 writeln 目前在内部调用的是 printf,因此它肯定不会被标记为 @safe,因为使用 printf 很容易出现不安全的情况(比如给它传递一个整数而不是字符串)。也许可以根据具体情况将 writeln 标记为 @trusted,但我不知道涉及哪些方面。这取决于它的实现方式。

任何非平凡的 D 程序都预计会使用 @system 代码。关键是要隔离它。你的程序中大多数部分都应该是 @safe 的,但其中一部分将必须是 @system 的。然而,你只需要检查程序的一小部分是否存在内存安全问题。一旦你手动验证了调用 @system 函数的函数确实是内存安全的,就可以将其标记为 @trusted,然后可以在 @safe 代码中使用它。

很不幸的是,由于在低级别的操作中使用了 @system ,因此 druntime 和 Phobos 中的某些核心部分很可能也会被标记为 @system,并且其中并非所有部分都已经按照应该有的方式标记为 @trusted (例如,std.array.appender 可能是 @system ,但实际上应该可以是 @trusted - 我不确定它当前的状态如何;它可能取决于数组的元素类型)。因此,为了更好地支持 @safe,一些标准库的改进可能需要进行(这正在进行中,但我不知道现在的情况),并且您可能需要现在在更多的地方使用 @trusted,而不是未来。 writeln 未来可能成为 @safe@trusted,但如果您使用它的类型没有 @safe@trustedtoString 函数,则无法实现。因此,无论如何实现,writeln 是否 @safe 取决于您使用它的方式,但即使对于内置类型,目前它也不是 @safe@trusted,所以现在您需要自己小心处理。

如果你真的想要,你可以创建一个writeln的包装器,并将其标记为@trusted,但你必须非常小心以确保代码实际上是内存安全的-仅仅创建一个模板化的包装器并将其标记为@trusted是不够的,因为你无论传递什么类型,它都会被视为@safe。所以,最好就是不包装它,然后如果你确定特定用法的writeln是内存安全的,就将调用者标记为@trusted。当然,这也突出了为什么像writeln这样的函数当前在第一时间被标记为@system的部分原因:往往很难编写@trusted的模板化代码而不信任不应该被信任的东西(因为它取决于模板参数)。属性推断通常会解决问题,但如果模板化代码正在执行需要@trusted的操作,则很难将代码的一部分标记为@trusted并将其余部分留给推断,特别是如果模板参数与@system相关的内容混合在一起。我预计我们最终将为所有标准库内容搞清楚这个问题。


2
这非常有道理 - 特别是在我看来,内存安全中最糟糕的罪魁祸首(指针)可以在大多数代码中避免使用(除了实际的低级函数)。 - Demi
2
@Demetri 没错。你的程序中有 @system 代码,但它是隔离的,并且通常只占很小一部分,因此自己验证其内存安全性是合理的。如果你完全没有 @safe,那么你就必须自己验证整个程序,这显然要困难得多。 - Jonathan M Davis
1
writefln 可以安全地实现,除了 char* 的自动打印可能会出现零终止符的情况(不过那实际上已经解决了吧?还是只是十六进制显示指针?)它根本不需要使用 printf - 我们已经有 std.format.formattedWrite 来完成大部分工作,然后最终调用 C 也可以用类似 fwrite() 这样非常理智的函数进行验证。至少可以放心使用。 - Adam D. Ruppe
2
@AdamD.Ruppe 我认为只要参数具有适当的 @safe 功能(例如 @safe toString),writeln 可能可以被安全地使用,但它不能简单地标记为 @trusted@safe,而且我不知道如何轻松地将适当的部分标记为 @trusted 而不将不应该是 @trusted 的部分标记为 @trusted。因此,根据实现方式,这可能会有点麻烦。不过,我相信我们可以做到。 - Jonathan M Davis

5

我认为我们应该把writeln及其他相关函数标记为@trusted - 虽然它们使用了较低级别的原语,但它们足够检查以确保例如printf没有收到混乱的参数。


你不能对自定义类调用 to!string 方法,可能不是 @safe 的。 - ratchet freak
1
这并不一定是个致命问题,因为它只会在某些模板参数下发生。我认为属性推断可以捕捉到这些情况。 - Adam D. Ruppe
1
能够用SafeD编写整个程序可能会非常不错。 - Demi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接