Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby에서 소박한 Nil 처리

지난 6개월 정도 저는 Rust에서 NES 에뮬레이터를 작업했습니다. 예상하시겠지만 저는 녹에 대해 많이 배웠고 NES 내부에 대해 더 많이 배웠습니다. 하지만 그 경험을 통해 Ruby를 바라보는 시각도 바뀌었습니다.

특히, nil을 반환하는 메서드에 대해 약간 편집증 이상으로 만들었습니다. .

당신이 무언가를 지지하지 않는다면 당신은 무엇이든 반하게 될 것입니다

nil의 기능 루비에서 의미? 거의 모든. 메서드가 nil을 반환하면 다음을 의미할 수 있습니다.

  • 메소드에 반환 값이 없습니다.
  • 보통 반환값이 있지만 이번에는 반환값이 없습니다.
  • 데이터베이스에서 NULL인 값을 반환합니다.
  • 예상치 못한 일이 발생했습니다

이것은 코드를 읽기 어렵게 만들고 가장 일반적인 Ruby의 Ruby 예외:NoMethodError . 예외 모니터링 서비스의 일부 소유자로서 NoMethodError 내 아이를 학교에 보내고 있습니다.

다음 코드를 보십시오. nil을 반환합니다. 대부분의 경우 if 때문에 명령문은 nil로 평가됩니다. 조건문이 일치하지 않고 else가 없는 경우 .

def color_name(rgb)
  if rgb == 0x000000
    "black"
  end
end

color_name("#FFFFFF").titleize
=> NoMethodError: undefined method `titleize' for nil:NilClass

경험 많은 Ruby 개발자라면 이 토끼굴이 훨씬 더 깊이 들어간다는 것을 알고 있습니다. 때때로 nil의 이러한 다른 의미는 이상한 방식으로 겹치므로 예를 들어 데이터베이스의 값이 NULL인지 여부를 알 수 없습니다. , 또는 데이터베이스에 값이 전혀 없습니다.

더 나은 방법

Rust에는 nil과 같은 것이 없습니다. . 대신, 함수가 때때로 값을 반환하고 때로는 "아무것도"를 반환하지 않는다는 것을 나타내려면 Option을 사용합니다. .

Options 특정 값을 포함하거나 값을 포함하지 않는 유형입니다. 코드에서는 다음과 같이 표시됩니다.

Option::Some(42); // Wraps the number 42 in an option
Option::None;     // Indicates "no result"

이것은 임시 nil보다 이미 좋아 보입니다. 사용하지만 더 좋아집니다. Rust 컴파일러는 강제로 None을 고려하도록 합니다. 사례. 실수로 무시할 수 없습니다.

match my_option {
  Some(x) => do_something_with_x(x),
  // If you remove the `None` match below, this code
  // won't compile.
  None => do_the_default_thing()  
}

따라서 다음과 같이 색상 명명 예제를 녹으로 작성할 수 있습니다.

fn color_name(rgb: u32) -> Option<String> {
    if rgb == 0x000000 {
      Some("black".to_owned())
    } else {
      None
    }
}

이제 SomeNone 조건:

let name = color_name(0xFFFFFF);

let name = match color_name(0xFFFFFF) {
  Some(value) => value,
  None => "unknown".to_owned(),
}

물론 이것은 약간 장황하고 이상하게 보이지만 함수가 유용한 값을 반환하지 않는 경우를 무시하는 것은 불가능합니다. 즉, 코드를 이해하고 유지 관리하기가 더 쉽습니다.

Ruby에서 옵션 구현

Ruby가 엄격함에서 부족한 부분을 유연성으로 보완합니다. Option과 같은 것을 구현하는 것이 재미있을 것이라고 생각했습니다. 루비에서.

Ruby는 해석된 언어이기 때문에 컴파일 타임 오류를 생성할 수 없습니다. 하지만 잘못된 코드가 항상 극단적인 경우에만 예외를 발생시키는 대신 예외를 발생시킵니다.

먼저 두 개의 클래스를 생성해 보겠습니다. Some 읽기 전용 값을 보유합니다. None 비었다. 그들은 보이는 것처럼 단순합니다.

  class Some
    attr_reader :value
    def initialize(value)
      @value = value
    end
  end

  class None
  end

다음으로 Option Some 또는 None 둘 다에 대한 핸들러를 제공할 때만 액세스할 수 있습니다.

class Option
  def initialize(value)
    @value = value
  end

  def self.some(value)
    self.new(Some.new(value))
  end

  def self.none()
    self.new(None.new)
  end

  def match(some_lambda, none_lambda)
    if @value.is_a?(Some)
      some_lambda.call(@value.value)
    elsif @value.is_a?(None)
      none_lambda.call()
    else
      raise "Option value must be either Some or None"
    end
 end
end

마지막으로 새로운 Option을 사용하도록 색상 예제를 다시 작성할 수 있습니다. 클래스:

def color_name(rgb)
  if rgb == 0x000000
    Option.some("black")
  else
    Option.none()
  end
end

puts color_name(0x000000).match(
  -> value { value },
  -> { "no match" })

# Prints "black"

결론

나는 아직 실제 프로젝트에서 이 기술을 시도하지 않았습니다. 많은 NoMethodErrors를 확실히 방지할 수 있다고 생각합니다. 항상 생산에 빠져들게 됩니다. 약간 성가신 모양이고 Rubyish하지 않지만 약간의 개선으로 더 즐거운 구문이 나타날 것이라고 생각합니다.