-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rust default arguments #3
base: master
Are you sure you want to change the base?
Conversation
Here's my two cents/ramblings. So, I think there's two kinds of cases for the optional arguments that I'm going to loosely call
let buf = OIIO::ImageBufAlgo::zero().set_roi(&roi).set_nthreads(0).run()?; As for the let image_input = OIIO::ImageInput::create(...)?;
let mut buffer: Vec<f32> = Vec::with_capacity(1024);
image_input.read_tile(x, y, z, OIIO::TypeDesc::FLOAT, &mut buffer).set_xstride(stride).set_ystride(stride).set_zstride(stride).run()?; However, the descriptor way seems to be cleaner in this case... I don't really have an answer one way or another. |
The other, much simpler option is just not to try and force default arguments into the Rust API at all. Personally, since using Rust for a few years I've come to much prefer explicitly listing all the arguments. The extra minute it takes me to type them out (or seconds if I'm using rust-analyzer) is nothing compared to the time everyone else who ever reads the code needs to spend examining the call site and mentally type-checking it to figure out exactly what it's going to do. I do find builders useful for filling out parameter structs that need to be sent through a C API (they're common in e.g. CUDA and OptiX). |
For simple structs with a few fields, the descriptors pattern seems ok, however for more complex types, the builder pattern is no doubt a much better solution. Also with descriptors, we have to either fill all the fields or pass This highly depends on the underlying ffi types, how complex they are, how are idiomatic Rust wrappers going to be generated by hand or automatic etc. We might choose to |
I get that, explicit is better for sure. But there are cases when I just want to get things done and I don't immediately care about a particular argument. wgpu-rs is an example where the api is so verbose I want it to give me loads of defaults that will get me started. I think the vfx platform libs are different (we're not wrapping vulkan). But things like: UsdStage::CreateNew (const std::string &identifier, const SdfLayerHandle &sessionLayer, const ArResolverContext &pathResolverContext, InitialLoadSet load=LoadAll) I just want to get going, with a file path. pub fn create_new(identifier : &str, session_layer=Option<sdf::LayerHandle>, path_resolver_context=Option<ar::ResolverCOntext>, load=Option<InitialLoadSet>) -> Self { ..} Then if you write none, your'e being explicit that you don't care about the other arguments for the moment. |
If it’s common enough that writing the extra arguments all the time is a
constant irritation, and anyone reading the code *expects* to see the
single-argument version most of the time, I’d normally prefer to split it
into two functions: one for the simple path, and one that takes the extra
configuration (e.g. _with_config() ).
…On Tue, 10 Nov 2020 at 12:16, Luke Titley ***@***.***> wrote:
The other, much simpler option is just not to try and force default
arguments into the Rust API at all. Personally, since using Rust for a few
years I've come to much prefer explicitly listing all the arguments. The
extra minute it takes me to type them out (or seconds if I'm using
rust-analyzer) is nothing compared to the time everyone else who ever reads
the code needs to spend examining the call site and mentally type-checking
it to figure out exactly what it's going to do.
I do find builders useful for filling out parameter structs that need to
be sent through a C API (they're common in e.g. CUDA and OptiX).
I get that, explicit is better for sure. But there are cases when I just
want to get things done and I don't immediately care about a particular
argument.
wgpu-rs is an example where the api is so verbose I want it to give me
loads of defaults that will get me started. I think the vfx platform libs
are different (we're not wrapping vulkan).
But things like:
UsdStage::CreateNew (const std::string &identifier, const SdfLayerHandle &sessionLayer, const ArResolverContext &pathResolverContext, InitialLoadSet load=LoadAll)
I just want to get going, with a file path.
Maybe it's enough to just say:
pub fn create_new(identifier : &str, session_layer=None, path_resolver_context=None, load=None) -> Self { ..}
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#3 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAOYQXMD2SEJDDRMTW4ZNHTSPBZ5FANCNFSM4TNVKP6A>
.
|
Based on a thread in #rust slack.
Luke Titley Nov 5th at 10:59 PM
hey, so I have a question about handling default parameters. So in c++ you have default parameters and function overloading that allows you to provide developers with defaults when calling a method. Rust doesn't have this, but there are two patterns I've seen that do the same sort of thing.
The builder, which uses chaining.
and descriptors
For me the builder is more concise but wgpu-rs for example uses descriptors. They say it's easier to read. Descriptors seems more rust like, since the state of the fields don't change once they are set.
I'm curious about what your experiences are, what are you guys are doing in your projects? (edited)
14 replies
Anders Langlands 1 day ago
I prefer builders. I find the only downside is the boilerplate needed to make them. I find the descriptor one much less readable with all the option stuff around it.
👍
1
Ryan Bottriell 1 day ago
go uses the descriptor pattern more, which I like: I like that it makes all options very clear the developer, but doesn't solve the default argument very well... builders on the other hand make default args simple, but sometimes it's not clear what is required and what is default or even what it available -
Overall with a builder you can make a function call that's missing required arguments and not know it until runtime - which I find annoying and un-rust-like so for that reason I vote descriptors, possibly with support for the Default::default() syntax
👍
2
Luke Titley 1 day ago
Required arguments in a Builder can be passed to the constructor of the builder.
Karthik 1 day ago
Java doesn't have default parameters too. So generally we use builders or factories. In the factory pattern we can have a factory class with separate methods for different set of parameters like createFromStartEndTime() or createFromEndTime(). I don't know if this pattern is used in rust.
But I prefer using builders. (edited)
👍
1
Ryan Bottriell 21 hours ago
fair points on the builder - I can't say it feels natural to send a builder to a function rather than have it build something, but I see how it works pretty well here
the other option that I think is worth noting here is just to use an Option in rust to denote optional args. You'd need to handle defaults in the body but could keep the signature more explicit over a builder - not that I'm leaning heavily either way
Scott Wilson 21 hours ago
I think usually the builder goes something like
Builder::new(...).add_thing(...).build()