Open bpsuntrup opened 6 years ago
You'd do this to improve readablity / auditability, so you'll frequently prefer the nested function call with a descriptive name and good comments. Also a macro could build code like:
{ fn tempname<A,B,C>(name1:A, name2: B, name3: C) {
} tempname(name1,name2,name3) }
A macro can't do this.
fn
item.fn
signature requires full knowledge of all types.Edit: Wait a second. I think I have a (terrible) idea...
Well, okay. I thought I had a prototype, but it barely does any of what I wanted it to:
fn
pointer).let x;
)&T
or &mut T
fn
pointer type and a closure)let a = vec![()];
let b = vec![()];
let mut c = vec![()];
let d = vec![()];
let mut e = vec![()];
using!{[b, mut c, ref d, ref mut e] {
let _ = b; // b is Vec<()>. It was moved.
c.sort(); // c is Vec<()>. It was moved.
let _ = d.iter(); // d is &Vec<()>
let _ = e.iter_mut(); // e is &mut Vec<()>
// let _ = a.iter(); // a cannot be used.
}}
Sigh. The only way to make it work really nice is with language support. There's simply no other way to check off the first two bullets.
Cute trick with the closure type inference rules.
An RFC for closure style type inference for _
types in nested fn
s makes this trivial, except not the first two points. And sounds vastly more useful.
fn bar() {
..
fn foo(x: _) { .. }
..
}
It's also much more convincing since you merely argue that nested fn
s should benefit the same ergonomics that closures do.
I still think: You're doing this for readability, so go the whole way and split up the long function or at least give your nested functions good names.
As an aside, if you want truly hackish then one could maybe do this latex style with a procedural macro that produced side effectual debug builds that wrote the type information to an .aux
file using std::intrinsics::type_name
:)
You can always just write a clippy lint that enforces the behaviour you want. It'll essentially come down to
#[clippy::close(mut x, move y)]
{
// modifying x and y possible within block. Everything else can only be read
// can modify Cell/Mutex though
}
Restricting the language is exactly what lints do. Especially our restriction
lints
@burdges when I factor out a function and give it a name, I'd rather it serve a purpose beyond just being a line drawn in the sand for readability. The reason for this is that, in code that is subject to frequent changes in requirements:
And thus factoring out a function to make the code cleaner in the short term can backfire in the long run, if done arbitrarily.
Oh, um, er... I think I misread your posts and didn't realize you were still advocating nesting the function body inside the original function.
In that case, my major annoyances are really
fn foo(x: _)
!)I do think nested fn foo(x: _)
sounds like an uncontroversial RFC, since closures already work that way.
#[clippy::close(mut x, move y)]
looks a lot like a similar Ada/SPARK feature.
If I am writing a long function, I often want to close off a chunk of code with
{ code_in_here }
so that local variables I declare within the chunk don't "escape". Example:However, inside the
{ }
, it's still possible to read (and even write to)x
. When reading long functions, I would like a way of assuring that a given complicated chunk of code only touches the variables I want it to touch.A solution:
This works, and does exactly what I need, but is rather ugly. I suggest a bit of built-in sugar for this use case. Perhaps rather than:
we would have:
And we can tell just by looking at one line exactly what variables this chunk of code touches and what its side effects are.
...or even better, a closure with controlled access to its enclosing scope that returns a value: