首页 > 解决方案 > 如何强制返回值与 Crystal 中的参数值类型相同?

问题描述

如果我有一个接受多种类型参数的函数,我如何强制返回必须与输入的值匹配?

当我想要一种方法与父类型的任何子代一起工作时,尤其会出现这种情况。为了演示,考虑一些“barlike”的东西:

abstract struct Barlike
  property bar: Int32
  def initialize(@bar); end
  end
  abstract def make_clang_sound
  abstract def serve_drinks
end

现在任何结构都可以实现这两种方法,并存储该值

struct Bar1 < Barlike
  def make_clang_sound
    puts "bing bang bong"
  end

  def serve_drinks
    puts "your drink sir"
  end
end

struct Bar2 < Barlike
  def make_clang_sound
    puts "kling klang"
  end

  def serve_drinks
    puts "here are your drinks"
  end
end

现在,如果我有一个方法想要使用 bar 并返回一个带有更新值的新方法(毕竟这些是结构):

def foo(arg : Barlike)
  new_bar = arg.bar + 2
  arg.class.new(new_bar)
end

这将返回 a Bar1if aBar1传入和 a Bar2if that 传入但不能保证:

def foo(arg : Barlike)
  "howdy"
end

我也将把我foo放入一个抽象结构中,所以我需要保证foo返回的实现者与Barlike给定的类型相同。

我试过了

def foo(arg : Barlike) : arg.class
end

但这是一个编译时错误(arg 不能在那里使用)

我也试过

def foo(arg : Barlike) : typeof(arg)
end

它通过了,但这里的 typeof 只是 Barlike 而我真的需要它只是传入的东西,只有 Bar1 或 Bar2 等等。

宏可以帮助吗?

标签: crystal-lang

解决方案


用于此的工具是自由变量。这本质上是针对单个方法的泛型。

# This method returns the same type as its argument
def foo(arg : T) : T forall T
  arg
end

这已经解决了您问题的主要部分。

但是,目前无法对自由变量应用类型限制,例如限制TBarlike.

不过,有一些解决方法:

  1. 使用宏来验证参数类型:
def foo(arg : T) : T forall T
  {% raise "arg must implement Barlike" unless T < Barlike %}
  arg
end
  1. 委托给另一个具有类型限制的方法:
def foo(arg : T) : T forall T
  foo_impl(arg)
end

private def foo_impl(arg : Barlike)
  arg
end

两种解决方法都会影响方法的实现。没有办法为 a 指定这样的类型限制abstract def。数字 2 可能是可行的,如果你做foo_impl抽象并需要继承类来实现这个,而不是foo. 但也可以只使用自由变量的初始示例,没有Barlike限制。在实践中,您可能不会获得太多收益。


推荐阅读