ในช่วงหกเดือนที่ผ่านมาฉันได้ใช้โปรแกรมจำลอง NES ใน Rust อย่างที่คุณคาดไว้ ฉันได้เรียนรู้มากมายเกี่ยวกับการเกิดสนิม และเพิ่มเติมเกี่ยวกับ NES ภายใน แต่ประสบการณ์ก็เปลี่ยนวิธีที่ฉันมอง Ruby ด้วย
โดยเฉพาะอย่างยิ่ง มันทำให้ฉันหวาดระแวงเล็กน้อยเกี่ยวกับวิธีการที่คืนค่า ไม่มี
.
ถ้าคุณไม่ยืนหยัดเพื่อบางสิ่ง คุณจะตกหลุมรักทุกสิ่ง
ไม่มี
อะไร ไม่มี
หมายถึงในทับทิม? เกือบทุกอย่าง เมื่อเมธอดคืนค่าเป็นศูนย์ อาจหมายถึง:
- เมธอดนี้ไม่มีค่าส่งคืน
- ปกติจะมีค่าส่งคืนแต่ไม่ใช่ครั้งนี้
- ส่งคืนค่าจากฐานข้อมูลซึ่งเป็น NULL
- มีเรื่องไม่คาดฝันเกิดขึ้น
ทำให้โค้ดอ่านยากและเป็นสาเหตุหลักของ ที่พบบ่อยที่สุด ข้อยกเว้น Ruby ใน Ruby:NoMethodError
. ในฐานะเจ้าของส่วนหนึ่งของบริการตรวจสอบข้อยกเว้น NoMethodError
กำลังส่งลูกเข้าโรงเรียน
ดูรหัสต่อไปนี้ คืนค่า ไม่มี
ส่วนใหญ่เพราะ ถ้า
คำสั่งประเมินเป็น ไม่มี
เมื่อเงื่อนไขไม่ตรงกันและไม่มี อื่น
.
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 ไม่มีสิ่งที่เรียกว่า ไม่มี
. เมื่อเราต้องการแสดงว่าบางครั้งฟังก์ชันคืนค่าและบางครั้งคืนค่า "ไม่มี" เราใช้ Option
.
ตัวเลือก
เป็นประเภทที่มีค่าเฉพาะหรือไม่มีค่า นี่คือหน้าตาของโค้ด:
Option::Some(42); // Wraps the number 42 in an option
Option::None; // Indicates "no result"
นี่ดูดีกว่าโฆษณาเฉพาะของเรา ไม่มี
การใช้งานแต่จะดียิ่งขึ้นไปอีก คอมไพเลอร์ Rust บังคับให้คุณพิจารณา ไม่มี
กรณี. คุณไม่สามารถละเลยได้โดยบังเอิญ
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
และ ไม่มี
เงื่อนไข:
let name = color_name(0xFFFFFF);
let name = match color_name(0xFFFFFF) {
Some(value) => value,
None => "unknown".to_owned(),
}
แน่นอนว่านี่ดูละเอียดและแปลกไปเล็กน้อย แต่ก็ทำให้ไม่สามารถเพิกเฉยต่อกรณีนี้เมื่อฟังก์ชันไม่คืนค่าที่เป็นประโยชน์ นั่นหมายถึงโค้ดที่เข้าใจและบำรุงรักษาได้ง่ายขึ้น
การนำตัวเลือกไปใช้ใน Ruby
สิ่งที่ Ruby ขาดความเข้มงวดนั้นชดเชยด้วยความยืดหยุ่น ฉันคิดว่ามันน่าสนใจที่จะลองใช้บางอย่างเช่น Option
ในทับทิม
เราไม่สามารถสร้างข้อผิดพลาดเวลาคอมไพล์ใน Ruby ได้ เนื่องจากเป็นภาษาที่ตีความ แต่เราสามารถทำให้รหัสไม่ถูกต้อง เสมอ ยกข้อยกเว้น แทนที่จะเพิ่มข้อยกเว้นเฉพาะเมื่อคุณพบกรณีขอบ
ขั้นแรก ให้สร้างสองคลาส บางส่วน
มีค่าอ่านอย่างเดียว ไม่มี
มันว่างเปล่า. เรียบง่ายอย่างที่คิด
class Some
attr_reader :value
def initialize(value)
@value = value
end
end
class None
end
ต่อไป เราจะสร้าง Option
คลาสที่มี บางส่วน
หรือ ไม่มี
และอนุญาตให้เราเข้าถึงได้ก็ต่อเมื่อเราให้ตัวจัดการสำหรับทั้งคู่
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 มากนัก แต่ฉันคิดว่าด้วยการปรับแต่งบางอย่าง ไวยากรณ์ที่น่าพึงพอใจจะปรากฏขึ้น