Ruby 分配一个变量或在 nil 时引发错误

问题描述

在 kotlin 和 C# 中,您可以分配一个变量,否则如果值为 nil,您可以使用 ?:?? 运算符抛出异常。

例如,在 C# 中:

var targetUrl = GetA() ?? throw new Exception("Missing A");
// alt
var targetUrl = GetA() ?? GetB() ?? throw new Exception("Missing A or B");

这在 ruby​​ 中可行吗?如果是这样,如何?

基本上,我想做的是这个

target_url = @maybe_a || @maybe_b || raise "either a or b must be assigned"

我知道我可以做到这一点

target_url = @maybe_a || @maybe_b
raise "either a or b must be assigned" unless target_url

但如果可能的话,我想在一行中完成

解决方法

基本上,我想做的是这个

target_url = @maybe_a || @maybe_b || raise "either a or b must be assigned"

您必须为 raise 添加括号才能使您的代码正常工作:

x = a || b || raise("either a or b must be assigned")

使用控制流运算符 or 而不是 || 会“更正确”:(这使得括号可选)

x = a || b or raise "either a or b must be assigned"

这是 Perl 的“do this or die” 成语,我认为它干净整洁。它强调了一个事实,即 raise 不为 x 提供结果 - 它只是因其副作用而被调用。

然而,有些人认为 or / and 令人困惑,根本不应该使用。 (见rubystyle.guide/#no-and-or-or

Rails 大量使用的一种模式是有两种方法,一种没有 !,它不提供错误处理:

def a_or_b
  @maybe_a || @maybe_b
end

和一个带有 ! 的函数:

def a_or_b!
  a_or_b || raise("either a or b must be assigned")
end

然后通过以下方式调用它:

target_url = a_or_b!
,

你可以用括号解决它:

(target_url = @maybe_a || @maybe_b) || raise("either a or b must be assigned")
,

使用运算符优先级

另一种以极少的代码更改完成您想要的操作的方法是使用 lower-precedence or 运算符,它的优先级低于 ||= .例如:

# This is closest to what you want,but violates many style guides.
target_url = @maybe_a || @maybe_b or raise "either a or b must be assigned"

您还可以在不改变其工作方式的情况下包装逻辑行,例如:

# Same code as above,but wrapped for line length
# and to clarify & separate its expressions.
target_url = @maybe_a || @maybe_b or
  raise "either a or b must be assigned"

无论哪种方式,代码都会按预期引发 RuntimeError 异常。由于优先规则,不需要括号。

请注意,许多 style guides like this one 会告诉您完全避免使用 or 运算符,或者仅将其用于流量控制,因为它通常是导致细微优先级错误的原因一目了然。话虽如此,包装版本实际上只是 my other answer 的倒置变体,并且在不使用括号的情况下很容易在视觉上区分,尤其是启用了语法突出显示。您的里程和风格指南的严格程度肯定会有所不同。

,

在赋值表达式中使用后缀条件

因为 Ruby 中的大部分内容都计算为一个表达式,您可以通过使用 unless 作为后缀条件后跟一个赋值表达式来将其作为单个逻辑行执行。我选择将线包裹起来以适应合理的线长,但如果您真的想要“单线”,请随意将其设为单线。例如:

raise "either a or b must be assigned" unless
  target_url = @maybe_a || @maybe_b

如您所料,这将正确引发 RuntimeError。

自动激活

请注意,这种特殊方法会自动激活 @maybe_a 并为其分配 nil。如果 @maybe_a 评估为 false,它也会对 @maybe_b 做同样的事情。虽然很方便,但如果您依赖 defined? 来识别代码中其他地方的未定义变量,自动激活可能会在以后绊倒您。因此,这个习语的优缺点取决于你更广泛的意图,但它肯定会在原始问题的范围内完成工作。