我想知道Rust语言中的Option::None
和其他编程语言中的"null"之间的主要区别是什么。为什么None
被认为更好?
T*
的话,它可以返回NULL
(在C语言中全部大写,因为它是一个宏),表示失败或缺少值。在像C这样的低级语言中,在那个时代,这种方式相当有效。我们才刚刚从汇编语言世界中脱颖而出,进入了有类型(并且相对安全)的语言世界。NULL
(稍后添加了nullptr
,它不是一个宏,是一个稍微更加类型安全的NULL
)。在Java中,问题变得更加明显:每个非原始值(基本上就是不是数字或字符的每个值)都可以是null
。如果我的函数以String
为参数,则我必须随时准备好处理某些幼稚的年轻程序员可能传递给我的null
情况。如果我从函数中获得String
作为结果,那么我必须检查文档以确定它是否存在。NullPointerException
,即使在今天,每天都有成千上万的年轻程序员因此陷入困境而涌向本网站寻求帮助。null
”不是一种可持续的方法。(动态类型语言通常在任何地方都更习惯于处理失败,因为基本上这是它们的天性,因此像Ruby中的nil
或Python中的None
之类的存在要略微容易接受些。)String
,那么我实际上有一个String
。如果我打算让我的String
是可空的,那么我可以使用String?
选择性地加入可空性,这是一种既可以是字符串又可以是null
的类型。关键是,这是类型安全的。如果我有一个String?
类型的值,那么在进行null
检查或做出断言之前,我不能调用它的String
方法。因此,如果x
具有String?
类型,则除非我首先执行以下操作之一,否则无法执行x.toLowerCase()
。
if (x!= null)
中,以确保x
不是null
(或者其他证明我的情况的控制流形式)。?
空安全调用运算符执行x?.toLowerCase()
。这将编译为if (x!= null)
检查,并返回一个String?
,如果原始字符串为null
,则该值为null
。!!
断言该值不为null
。如果我错了,这个断言会被检查并抛出异常。请注意,(3)是Java默认情况下的做法。不同之处在于,在Kotlin中,“我断言我比类型检查器更懂”这种情况是可选的,你必须费点心思才能进入可能导致空指针异常的情况。(我略过了平台类型,这是类型系统中一种方便的黑客方式,用于与Java互操作。这里并不重要)
Nullable types 是 Kotlin 解决这个问题的方式,Typescript(使用--strict
模式)和 Scala 3(打开 null 检查)也采用了类似的方式。然而,在 Rust 中实现这种方式需要对编译器进行重大改变,因为 nullable types 要求语言支持 子类型。在像 Java 或 Kotlin 这样首先使用面向对象原则构建的语言中,引入新的子类型很容易。例如,String
是 String?
的子类型,同时也是 Any
(基本上就是 java.lang.Object
)和 Any?
(任何值以及 null
)的子类型。因此,像 "A"
这样的字符串具有 String
的 principal type,但也通过子类型拥有所有这些其他类型。String
是 dyn Display
的 subtype,只能说一个 String
(在非定长的情况下)可以自动转换为 dyn Display
。x
有一个 “最佳” 类型 T
。在具有子类型的面向对象语言中,x
可能有其他类型,这些类型是 T
的 supertypes。然而,在没有子类型的函数式语言中,x
确实只有一个类型。还有其他可以与 T
unify 的类型,例如(使用 Haskell 的符号)forall a. a
。但不能真正地说 x
的类型是 forall a. a
。“abc”
同时是 String
和 String?
,而 null
只是 String?
。由于我们没有子类型化,因此需要为 String
和 String?
情况分别使用两个不同的值。struct Foo(i32);
那么Foo(0)
是Foo
类型的一个值。没有什么好说的,它不是一个Foo?
或可选的Foo
之类的东西。它只有一种类型。
然而,还有一个相关的值叫做Some(Foo(0))
,它是一个Option<Foo>
。请注意,Foo(0)
和Some(Foo(0))
不是同一个值,它们只是以一种相当自然的方式相关。区别在于,虽然Foo
必须存在,但Option<Foo>
可以是Some(Foo(0))
,也可以是None
,这有点像Kotlin中的null
。我们仍然必须检查该值是否为None
,然后才能执行任何操作。在Rust中,我们通常使用模式匹配或者使用几个内置函数来进行模式匹配。这是相同的思想,只是使用了函数式编程技术来实现。
因此,如果我们想要从一个Option
中获取值,或者如果它不存在,则获取默认值,我们可以编写:
my_option.unwrap_or(0)
my_option.and_then(|inner_value| ...)
?.
在 Kotlin 中的基本作用是什么。如果我们想要断言一个值存在,否则就会出现错误,我们可以这样写:
my_option.unwrap()
match my_option {
None => {
...
}
Some(value) => {
...
}
}
String?
或 Int?
的函数,而不必担心捆绑它们或不断检查它们是否在 Some
中。Option
,则某人可以自己用几行代码编写它。它不是特殊语法,只是存在并且有一些(普通)函数定义在其上的类型。它也是由组合构建的,这意味着(自然地)它更好地组成。也就是说,(T?)?
等价于 T?
(前者仍然只有一个 null
值),而 Option<Option<T>>
不同于 Option<T>
。我不建议直接编写返回 Option<Option<T>>
的函数,但在编写通用函数时可能会受到此影响(即您的函数返回 S?
,并且调用者恰好使用 Int?
实例化 S
)。nil
、undef
和none
)都是相同的。它们并不相同。undef
),以及表示没有值(Rust的Option::none
)。undef
,Go和Ruby有nil
,Python有None
。它们很有用,因为它们与false或0不同。
越来越多的语言使用特殊对象和类型表示空值。它们拥有关键字的所有优点,但不像通用的“出现错误”那样,它们可以在每个领域具有非常具体的意义。它们还可以是用户定义的,提供了超出语言设计者预期的灵活性。并且,如果您使用对象,则可以附加其他信息。
例如,在 Rust 中,std::ptr::null
表示空原始指针。Go 有 error
接口。Rust 有 Result::Err
和 Option::None
。
大多数编程语言使用二值或二进制逻辑:真或假。但一些特别是SQL,使用三值或三进制逻辑:真、假和未知。在这里,null意味着“我们不知道值是什么”。它既不是真也不是假,也不是错误、空、零:该值是未知的。这改变了逻辑的工作方式。
如果将任何东西与未知值进行比较,甚至是自身,结果都是未知的。这使您可以基于不完整的数据得出逻辑结论。例如,Alice身高7'4'',我们不知道Bob有多高。Alice比Bob高吗?未知。
当您声明一个变量时,它必须包含某些值。即使在C语言中,它以前所说的不会为您初始化变量,它仍然包含内存中存在的任何值。现代语言将为您初始化值,但它们必须将其初始化为某些内容。通常,这种内容是null。
for thing := range things { ... }
,当范围中没有更多项时,range
将返回nil
,导致for
循环退出。虽然这更灵活,但它与经典哨兵具有相同的问题:你需要一个永远不会出现在列表中的值。如果null是有效值呢?StopIteration
,它们的循环将捕获并退出循环。这避免了选择哨兵值的问题,Python和Ruby迭代器可以返回任何内容。error
。然后这两个值可以独立检查。Rust只能返回单个值,因此它通过返回特殊类型Result
来解决这个问题。它包含一个带有返回值的Ok
或带有错误代码的Err
。Result::Err
具有特殊类型的额外优势,您无法意外地将Result::Err
用作其他任何东西。
get
将在键不存在时返回None
。ArgumentError
。例如,array.first(-2)
要求获取前-2个值,这是无意义的,并将引发ArgumentError
。
这最终将我们带回到Option :: None
。它是null的“特殊类型”版本,具有许多优点。
Rust在许多方面使用Option
: 指示未初始化的值,指示简单错误,作为无值和Rust特定事物。文档提供了许多示例。
在这么多地方使用它会削弱它作为特殊类型的价值。它还与Result
重叠,这是您应该用来从函数返回结果的内容。
Result<Option<T>, E>
来区分三种可能性:有效结果、无结果和错误。Jeremy Fitzhardinge提供了一个很好的示例,说明从键/值存储中查询返回什么结果。
......我非常支持返回
Result<Option<T>, E>
,其中Ok(Some(value))
表示“这就是你要找的东西”,Ok(None)
表示“你要找的东西肯定不存在”,而Err(...)
则表示“我无法确定你要找的东西是否存在,因为出现了一些问题”。
null
的含义是相同的,但事实并非如此。很多语言也不把它称作null
,比如 Ruby 中的nil
,Perl 中的undef
。你能给一些其他编程语言中的 null 的例子吗? - Schwern