เป็นไปได้ไหมที่ Rust จะสร้างฟังก์ชันที่มีอาร์กิวเมนต์เริ่มต้น
fn add(a: int = 1, b: int = 2) { a + b }
เป็นไปได้ไหมที่ Rust จะสร้างฟังก์ชันที่มีอาร์กิวเมนต์เริ่มต้น
fn add(a: int = 1, b: int = 2) { a + b }
Option
None
คำตอบ:
ไม่มันไม่ได้อยู่ในปัจจุบัน ฉันคิดว่ามันน่าจะถูกนำมาใช้ในที่สุด แต่ปัจจุบันยังไม่มีงานที่ทำอยู่ในพื้นที่นี้
เทคนิคทั่วไปที่ใช้ในที่นี้คือการใช้ฟังก์ชันหรือวิธีการที่มีชื่อและลายเซ็นต่างกัน
เนื่องจากไม่รองรับอาร์กิวเมนต์เริ่มต้นคุณจะได้รับพฤติกรรมที่คล้ายกันโดยใช้ Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
สิ่งนี้บรรลุวัตถุประสงค์ของการมีค่าเริ่มต้นและฟังก์ชันที่เข้ารหัสเพียงครั้งเดียว (แทนที่จะเป็นในทุกการโทร) แต่แน่นอนว่าต้องพิมพ์ออกมาอีกมาก การเรียกใช้ฟังก์ชันจะมีลักษณะอย่างไรadd(None, None)
ซึ่งคุณอาจชอบหรือไม่ชอบขึ้นอยู่กับมุมมองของคุณ
หากคุณเห็นว่าไม่มีการพิมพ์อะไรเลยในรายการอาร์กิวเมนต์เนื่องจากผู้เขียนโค้ดอาจลืมเลือกข้อได้เปรียบใหญ่ที่นี่เป็นพยานอย่างชัดเจน ผู้โทรบอกอย่างชัดเจนว่าต้องการใช้ค่าเริ่มต้นของคุณและจะได้รับข้อผิดพลาดในการคอมไพล์หากพวกเขาไม่ใส่อะไรเลย add(DefaultValue, DefaultValue)
คิดว่ามันเป็นพิมพ์
คุณยังสามารถใช้มาโคร:
fn add(a: i32, b: i32) -> i32 {
a + b
}
macro_rules! add {
($a: expr) => {
add($a, 2)
};
() => {
add(1, 2)
};
}
assert_eq!(add!(), 3);
assert_eq!(add!(4), 6);
ความแตกต่างใหญ่ระหว่างสองโซลูชันคือเมื่อมีอาร์กิวเมนต์ "Option" -al จะสามารถเขียนได้อย่างสมบูรณ์add(None, Some(4))
แต่ด้วยรูปแบบมาโครที่ตรงกันคุณไม่สามารถทำได้ (ซึ่งคล้ายกับกฎอาร์กิวเมนต์เริ่มต้นของ Python)
คุณยังสามารถใช้โครงสร้าง "อาร์กิวเมนต์" และFrom
/ Into
ลักษณะ:
pub struct FooArgs {
a: f64,
b: i32,
}
impl Default for FooArgs {
fn default() -> Self {
FooArgs { a: 1.0, b: 1 }
}
}
impl From<()> for FooArgs {
fn from(_: ()) -> Self {
Self::default()
}
}
impl From<f64> for FooArgs {
fn from(a: f64) -> Self {
Self {
a: a,
..Self::default()
}
}
}
impl From<i32> for FooArgs {
fn from(b: i32) -> Self {
Self {
b: b,
..Self::default()
}
}
}
impl From<(f64, i32)> for FooArgs {
fn from((a, b): (f64, i32)) -> Self {
Self { a: a, b: b }
}
}
pub fn foo<A>(arg_like: A) -> f64
where
A: Into<FooArgs>,
{
let args = arg_like.into();
args.a * (args.b as f64)
}
fn main() {
println!("{}", foo(()));
println!("{}", foo(5.0));
println!("{}", foo(-3));
println!("{}", foo((2.0, 6)));
}
เห็นได้ชัดว่าตัวเลือกนี้มีโค้ดมากกว่า แต่ต่างจากการออกแบบมาโครที่ใช้ระบบ type ซึ่งหมายความว่าข้อผิดพลาดของคอมไพเลอร์จะเป็นประโยชน์กับผู้ใช้ไลบรารี / API ของคุณมากกว่า นอกจากนี้ยังช่วยให้ผู้ใช้สามารถFrom
นำไปใช้งานได้ด้วยตนเองหากเป็นประโยชน์กับพวกเขา
ไม่ Rust ไม่รองรับอาร์กิวเมนต์ของฟังก์ชันเริ่มต้น คุณต้องกำหนดวิธีการต่างๆด้วยชื่อที่แตกต่างกัน ไม่มีฟังก์ชันโอเวอร์โหลดเช่นกันเนื่องจาก Rust ใช้ชื่อฟังก์ชันเพื่อสืบทอดประเภท (การโอเวอร์โหลดฟังก์ชันต้องการสิ่งที่ตรงกันข้าม)
ในกรณีของการกำหนดค่าเริ่มต้นของโครงสร้างคุณสามารถใช้ไวยากรณ์การปรับปรุงโครงสร้างดังนี้:
use std::default::Default;
#[derive(Debug)]
pub struct Sample {
a: u32,
b: u32,
c: u32,
}
impl Default for Sample {
fn default() -> Self {
Sample { a: 2, b: 4, c: 6}
}
}
fn main() {
let s = Sample { c: 23, .. Sample::default() };
println!("{:?}", s);
}
[ตามคำขอฉันโพสต์คำตอบนี้ข้ามจากคำถามที่ซ้ำกัน]
Rust ไม่รองรับอาร์กิวเมนต์ของฟังก์ชันเริ่มต้นและฉันไม่เชื่อว่าจะมีการใช้งานในอนาคต ดังนั้นฉันจึงเขียน proc_macro duangเพื่อใช้งานในรูปแบบมาโคร
ตัวอย่างเช่น:
duang! ( fn add(a: i32 = 1, b: i32 = 2) -> i32 { a + b } );
fn main() {
assert_eq!(add!(b=3, a=4), 7);
assert_eq!(add!(6), 8);
assert_eq!(add(4,5), 9);
}
หากคุณใช้ Rust 1.12 หรือใหม่กว่าอย่างน้อยคุณก็สามารถทำให้อาร์กิวเมนต์ของฟังก์ชันใช้งานได้ง่ายขึ้นด้วยOption
และinto()
:
fn add<T: Into<Option<u32>>>(a: u32, b: T) -> u32 {
if let Some(b) = b.into() {
a + b
} else {
a
}
}
fn main() {
assert_eq!(add(3, 4), 7);
assert_eq!(add(8, None), 8);
}
อีกวิธีหนึ่งคือการประกาศ enum โดยมีพารามิเตอร์เสริมเป็นตัวแปรซึ่งสามารถกำหนดพารามิเตอร์เพื่อใช้ประเภทที่เหมาะสมสำหรับแต่ละตัวเลือกได้ ฟังก์ชันนี้สามารถนำไปใช้เพื่อแบ่งส่วนความยาวตัวแปรของตัวแปร enum สามารถเรียงลำดับและความยาวได้ ค่าเริ่มต้นจะถูกนำไปใช้ภายในฟังก์ชันเป็นการกำหนดเบื้องต้น
enum FooOptions<'a> {
Height(f64),
Weight(f64),
Name(&'a str),
}
use FooOptions::*;
fn foo(args: &[FooOptions]) {
let mut height = 1.8;
let mut weight = 77.11;
let mut name = "unspecified".to_string();
for opt in args {
match opt {
Height(h) => height = *h,
Weight(w) => weight = *w,
Name(n) => name = n.to_string(),
}
}
println!(" name: {}\nweight: {} kg\nheight: {} m",
name, weight, height);
}
fn main() {
foo( &[ Weight(90.0), Name("Bob") ] );
}
เอาต์พุต:
name: Bob
weight: 90 kg
height: 1.8 m