지난 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
}
}
이제 Some
및 None
조건:
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하지 않지만 약간의 개선으로 더 즐거운 구문이 나타날 것이라고 생각합니다.