rust-unofficial / patterns

A catalogue of Rust design patterns, anti-patterns and idioms
https://rust-unofficial.github.io/patterns/
Mozilla Public License 2.0
7.98k stars 363 forks source link

Discussion: Functions with many parameters #87

Closed willow385 closed 3 years ago

willow385 commented 5 years ago

Requesting a design pattern for functions that take lots of parameters. For example this way:

fn foo(parameter_0: type, parameter_1: type, parameter_2: type, parameter_3: type, parameter_4: type, parameter_5: type, parameter_6: type) -> type {
    // function body
}

is a way that I think should be an anti-pattern because it's too wide. I prefer things to be less than 80 chars wide.

twe4ked commented 4 years ago

My first thought would be, would it make sense for the parameters to be part of a struct that you construct and pass in? It would depend on what the actual parameters represent of course.

Another option could potentially be the builder pattern. Something like this:

foo()
    .parameter_0(1)
    .parameter_1(2)
    .parameter_2(3)
    .parameter_3(4)
    .parameter_4(5)
    .parameter_5(6)
    .parameter_6(7)
    .build();

EDIT: See https://github.com/rust-unofficial/patterns/issues/64 for reasons against builder patterns for some use cases.

burdges commented 4 years ago
pub struct DoSomething<..> { ..paramaters.. }
impl<..> DoSomething<..> {
    pub fn go(self) { ... }
}

And choose the parameter names well, so that you can maximize field puns.

If you're okay with nightly then #![feature(unboxed_closures)] and #![feature(fn_traits)] let you write DoSomething { .. }() but imho that's kinda a poor reason for going to nightly.

longfellowone commented 3 years ago

My first thought would be, would it make sense for the parameters to be part of a struct that you construct and pass in? It would depend on what the actual parameters represent of course.

Another option could potentially be the builder pattern. Something like this:

foo()
    .parameter_0(1)
    .parameter_1(2)
    .parameter_2(3)
    .parameter_3(4)
    .parameter_4(5)
    .parameter_5(6)
    .parameter_6(7)
    .build();

EDIT: See #64 for reasons against builder patterns for some use cases.

And how would you deal with mandatory parameters?

twe4ked commented 3 years ago

And how would you deal with mandatory parameters?

I don't think I'd use it in that case.

pickfire commented 3 years ago

@longfellowone Do you have a required flow of the parameters?

Then there could be multiple structs (like a state machine) to ensure them have each mandatory parameter. Like most of the http crates that we have, it is required to have a url before sending.

MarcoIeni commented 3 years ago

And how would you deal with mandatory parameters?

foo("mandatory1", "mandatory2")
    .parameter_0(1)
    .parameter_1(2)
    .parameter_2(3)
    .parameter_3(4)
    .parameter_4(5)
    .parameter_5(6)
    .parameter_6(7)
    .build();
MarcoIeni commented 3 years ago

I am thinking about how to solve this issue. Maybe we can have an anti-pattern called "Too Many Arguments In Functions"? It could explain all the approaches proposed in this issue.

longfellowone commented 3 years ago

And how would you deal with mandatory parameters?

foo("mandatory1", "mandatory2")
    .parameter_0(1)
    .parameter_1(2)
    .parameter_2(3)
    .parameter_3(4)
    .parameter_4(5)
    .parameter_5(6)
    .parameter_6(7)
    .build();

What I have is a mathematical calculation that requires about 7 mandatory parameters that have no reasonable defaults. And 5 parameters that are optional

MarcoIeni commented 3 years ago

Put those 7 parameters in a struct and give the struct to foo