Trong Rust có thể tạo một hàm với một đối số mặc định không?
fn add(a: int = 1, b: int = 2) { a + b }
Trong Rust có thể tạo một hàm với một đối số mặc định không?
fn add(a: int = 1, b: int = 2) { a + b }
Option
và chuyển một cách rõ ràng None
.
Câu trả lời:
Không, nó không phải là hiện tại. Tôi nghĩ rằng nó có khả năng cuối cùng sẽ được thực hiện, nhưng không có hoạt động tích cực nào trong không gian này hiện tại.
Kỹ thuật điển hình được sử dụng ở đây là sử dụng các hàm hoặc phương thức có tên và chữ ký khác nhau.
Vì các đối số mặc định không được hỗ trợ, bạn có thể nhận được một hành vi tương tự bằng cách sử dụng Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
Điều này hoàn thành mục tiêu của việc có giá trị mặc định và hàm chỉ được mã hóa một lần (thay vì trong mỗi lần gọi), nhưng tất nhiên là nhiều hơn thế nữa để nhập. Lệnh gọi hàm sẽ trông như thế nào add(None, None)
, bạn có thể thích hoặc không tùy theo quan điểm của bạn.
Nếu bạn không thấy nhập gì trong danh sách đối số vì người viết mã có khả năng quên lựa chọn thì lợi thế lớn ở đây là tính rõ ràng; người gọi đang nói rõ ràng rằng họ muốn sử dụng giá trị mặc định của bạn và sẽ gặp lỗi biên dịch nếu họ không đặt gì. Hãy nghĩ về nó như đánh máy add(DefaultValue, DefaultValue)
.
Bạn cũng có thể sử dụng macro:
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);
Sự khác biệt lớn giữa hai giải pháp là với các đối số -al của "Option" hoàn toàn hợp lệ để viết add(None, Some(4))
, nhưng với mẫu macro phù hợp thì bạn không thể (điều này tương tự như các quy tắc đối số mặc định của Python).
Bạn cũng có thể sử dụng cấu trúc "đối số" và From
/ Into
đặc điểm:
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)));
}
Sự lựa chọn này rõ ràng là nhiều mã hơn, nhưng không giống như thiết kế macro, nó sử dụng hệ thống loại có nghĩa là các lỗi trình biên dịch sẽ hữu ích hơn cho người dùng thư viện / API của bạn. Điều này cũng cho phép người dùng From
thực hiện triển khai của riêng họ nếu điều đó hữu ích cho họ.
Không, Rust không hỗ trợ các đối số hàm mặc định. Bạn phải xác định các phương thức khác nhau với các tên khác nhau. Không có chức năng quá tải, bởi vì Rust sử dụng tên chức năng để dẫn xuất các loại (chức năng quá tải yêu cầu ngược lại).
Trong trường hợp khởi tạo cấu trúc, bạn có thể sử dụng cú pháp cập nhật cấu trúc như sau:
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);
}
[theo yêu cầu, tôi đã đăng chéo câu trả lời này từ một câu hỏi trùng lặp]
Rust không hỗ trợ các đối số hàm mặc định và tôi không tin rằng nó sẽ được triển khai trong tương lai. Vì vậy, tôi đã viết một proc_macro duang để triển khai nó ở dạng macro.
Ví dụ:
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);
}
Nếu bạn đang sử dụng Rust 1.12 trở lên, ít nhất bạn có thể làm cho các đối số hàm dễ sử dụng hơn với Option
và 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);
}
Một cách khác có thể là khai báo một enum với các tham số tùy chọn dưới dạng các biến thể, có thể được tham số hóa để lấy đúng loại cho mỗi tùy chọn. Hàm có thể được triển khai để lấy một lát cắt có độ dài thay đổi của các biến thể enum. Chúng có thể theo thứ tự và độ dài bất kỳ. Các giá trị mặc định được thực hiện trong hàm dưới dạng các nhiệm vụ ban đầu.
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") ] );
}
đầu ra:
name: Bob
weight: 90 kg
height: 1.8 m