局限性、与 CRuby 的差异

译者Lanza Schneider

mruby 的理念是对 Ruby ISO 标准的一个轻量级实现,而这两个目标的某些部分是矛盾的。Ruby 是一种有着复杂实现细节的表达式语言,这也让它很难以轻量级的方式实现。因此我们给出 "Ruby 兼容性" 的说明。

本文档还在收集关于局限性的例子。

完整性

首先说明,本文章并不包含完整的局限性列表。 如果您有新的发现,请提交给 mruby repo 帮助改进它。

Array 类型传递给 puts

将一个数组传给 puts ,会有不同的输出。

puts [1,2,3]

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

1
2
3

mruby [3.0.0 (2021-03-05)]

[1, 2, 3]

Kernel.raise 在 rescue 子句中

Kernel.raise 位于 rescue 子句且在没有参数的情况下不会抛出当前异常。

begin
  1 / 0
rescue
  raise
end

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

ZeroDivisionError is raised.

mruby [3.0.0 (2021-03-05)]

并没有抛出异常,除非你改成下面这样:

begin
  1 / 0
rescue => e
  raise e
end

Fiber 的执行不能跨越 C 函数的边界

mruby 的 Fiber 实现方式和 lua 的 coroutine 类似。这就导致你不能在 C 函数中切换上下文。 唯一的例外情况是在函数返回时调用 mrb_fiber_yield

Array 不支持实例变量

为了减少内存消耗, Array 不支持实例变量。

class Liste < Array
  def initialize(str = nil)
    @feld = str
  end
end

p Liste.new "foobar"

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

[]

mruby [3.0.0 (2021-03-05)]

抛出 ArgumentError

方法可见性

为了简单起见,并没有实现方法的可见性 (public/private/protected) 。尽管确实有相关方法的定义,但它们只是(为了保证兼容性的)虚假的空方法。

class VisibleTest

  def public_method; end

  private
  def private_method; end

end

p VisibleTest.new.respond_to?(:private_method, false)
p VisibleTest.new.respond_to?(:private_method, true)

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

false
true

mruby [3.0.0 (2021-03-05)]

true
true

可见性声明

下列可见性声明的形式并没有支持。

  • public
  • private
  • protected
  • module_function

特别地, module_function 不是一个虚假的空方法,但也没有实际效果。

module TestModule
  module_function
  def test_func
    p 'test_func called'
  end

  test_func
end

p 'ok'

Ruby [ruby 2.5.5p157 (2019-03-15 revision 67260)]

ok

mruby [3.0.0 (2021-03-05)]

test.rb:8: undefined method 'test_func' (NoMethodError)

defined?

defined? 关键字太复杂,无法完全实现。建议使用 const_defined? 和其它反射方法代替。

defined?(Foo)

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

nil

mruby [3.0.0 (2021-03-05)]

抛出 NameError

全局变量的 alias

全局变量的 alias 在 CRuby 可以使用,但是它不是 Ruby ISO 标准的一部分。

alias $a $__a__

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

nil

mruby [3.0.0 (2021-03-05)]

Syntax error

操作符重写

操作符无法被用户覆盖。

class String
  def +
  end
end

'a' + 'b'

Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]

抛出 ArgumentError。 The re-defined + operator does not accept any arguments.

mruby [3.0.0 (2021-03-05)]

'ab' 可以看到操作符的行为并没有变更。

Kernel#binding 不支持

Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]

$ ruby -e 'puts Proc.new {}.binding'
#<Binding:0x00000e9deabb9950>

mruby [3.0.0 (2021-03-05)]

$ ./bin/mruby -e 'puts Proc.new {}.binding'
trace (most recent call last):
        [0] -e:1
-e:1: undefined method 'binding' (NoMethodError)

关键字参数

mruby 关键字参数的行为与 CRuby 2.5 有一些不同,使得行为更简单,减少混乱。

Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]

$ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
[[{"a"=>1}], {:b=>2}]

mruby [3.0.0 (2021-03-05)]

$ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
trace (most recent call last):
    [0] -e:1
-e:1: keyword argument hash with non symbol keys (ArgumentError)

nil? 在条件表达式中的重定义

在条件表达式中,nil? 的重定义被忽略。

a = "a"
def a.nil?
  true
end
puts(a.nil? ? "truthy" : "falsy")

Ruby 输出 falsy;mruby 则输出 truthy

参数分解

def m(a,(b,c),d); p [a,b,c,d]; end
m(1,[2,3],4)  # => [1,2,3,4]

结构化参数 (上面示例代码的 bc ) 不能从可选参数和关键字参数的默认表达式中访问,因为实际的赋值是在这些默认表达式的计算完毕之后进行的:

def f(a,(b,c),d=b)
  p [a,b,c,d]
end
f(1,[2,3])

CRuby 输出 [1,2,3,nil];mruby 则抛出 NoMethodError 异常(b).