with_locals 0.3.2

Function attribute to return references to locals by using CPS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# `::with_locals`

[![Repository](https://img.shields.io/badge/repository-GitHub-brightgreen.svg)](https://github.com/danielhenrymantilla/with_locals.rs)
[![Latest version](https://img.shields.io/crates/v/with_locals.svg)](https://crates.io/crates/with_locals)
[![Documentation](https://docs.rs/with_locals/badge.svg)](https://docs.rs/with_locals)
[![MSRV](https://img.shields.io/badge/MSRV-1.65.0-white)](https://gist.github.com/danielhenrymantilla/8e5b721b3929084562f8f65668920c33)
[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
[![License](https://img.shields.io/crates/l/with_locals.svg)](https://github.com/danielhenrymantilla/with_locals.rs/blob/master/LICENSE)
[![CI](https://github.com/danielhenrymantilla/with_locals.rs/workflows/CI/badge.svg)](https://github.com/danielhenrymantilla/with_locals.rs/actions)

### CPS sugar in Rust, to "return" values referring to locals.

Let's start with a basic example: returning / yielding a `format_args` local.

```rust
use ::core::fmt::Display;
use ::with_locals::with;

#[with('local)]
fn hex (n: u32) -> &'local dyn Display
{
    &format_args!("{:#x}", n)
}
```

The above becomes:

```rust
use ::core::fmt::Display;

fn with_hex <R, F> (n: u32, f: F) -> R
where           F : FnOnce(&'_     dyn Display) -> R,
 // for<'local> F : FnOnce(&'local dyn Display) -> R,
{
    f(&format_args!("{:#x}", n))
}
```

`f: F`, here, is called a continuation:
instead of having a function return / yield some element / object,
the function takes, instead, the "logic" of what the caller would have liked
to do with that element (once it would have received it), so that it is the
callee who handles that object instead.

By shifting the logic like so, it is the callee and not the caller
who runs that logic, **which thus happens before the callee returns,
so before it cleans its locals and makes things that refer to it dangle.**

> This is the whole point of all this strategy!

Now, to call / use the above function, one can no longer bind the "result"
of that function to a variable using a `let` binding, since that mechanism
is reserved for actual returns, and the actual code running in the caller's
stack.

Instead, one calls / uses that `with_hex` function using
closure / callback syntax:

```rust,ignore
with_hex(66, |s| {
    println!("{}", s);
})
```

This is extremely powerful, but incurs in a rightward drift everytime
such a binding is created:

```rust,ignore
with_hex(1, |one| {
    with_hex(2, |two| {
        with_hex(3, |three| {
            // ughhh ..
        })
    })
})
```

Instead, it would be nice if the compiler / the language provided a way
for `let` bindings to magically perform that transformation:

```rust,ignore
let one = hex(1);
let two = hex(2);
let three = hex(3);
```

Operating in this fashion is called Continuation-Passing Style, and
cannot be done implicitly in Rust.
But that doesn't mean one cannot get sugar for it.

## Enters `#[with]`!

```rust,ignore
#[with] let one = hex(1);
#[with] let two = hex(2);
#[with] let three = hex(3);
```

  - This can also be written as:

    ```rust,ignore
    let one: &'ref _ = hex(1);
    let two: &'ref _ = hex(2);
    let three: &'ref _ = hex(3);
    ```

    That is, `let` bindings that feature a ["special lifetime"].

When applied to a function, it will tranform all its so-annotated
`let` bindings into nested closure calls, where all the statements that
follow the binding (within the same scope) are moved into the
continuation.

Here is an example:

```rust
# use ::with_locals::with; #[with] fn hex (n: u32) -> &'ref dyn ::core::fmt::Display { &format_args!("{:#x}", n) }
#
#[with]
fn hex_example ()
{
    let s: String = {
        println!("Hello, World!");
        #[with]
        let s_hex = hex(66);
        println!("s_hex = {}", s_hex); // Outputs `s_hex = 0x42`
        let s = s_hex.to_string();
        assert_eq!(s, "0x42");
        s
    };
    assert_eq!(s, "0x42");
}
```

The above becomes:

```rust
# use ::with_locals::with; #[with] fn hex (n: u32) -> &'ref dyn ::core::fmt::Display { &format_args!("{:#x}", n) }
#
fn hex_example ()
{
    let s: String = {
        println!("Hello, World!");
        with_hex(66, |s_hex| {
            println!("s_hex = {}", s_hex); // Outputs `s_hex = 0x42`
            let s = s_hex.to_string();
            assert_eq!(s, "0x42");
            s
        })
    };
    assert_eq!(s, "0x42");
}
```

#### Trait methods

Traits can have `#[with]`-annotated methods too.

```rust,ignore
# use ::with_locals::with;
#
trait ToStr {
    #[with('local)]
    fn to_str (self: &'_ Self) -> &'local str
    ;
}
```

Example of an implementor:

```rust
# use ::with_locals::with; trait ToStr { #[with] fn to_str (self: &'_ Self) -> &'ref str ; }
#
impl ToStr for u32 {
    #[with('local)]
    fn to_str (self: &'_ u32) -> &'local str
    {
        let mut x = *self;
        if x == 0 {
            // By default, the macro tries to be quite smart and replaces
            // both implicitly returned and explicitly returned values, with
            // what the actual return of the actual `with_...` function must
            // be: `return f("0");`.
            return "0";
        }
        let mut buf = [b' '; 1 + 3 + 3 + 3]; // u32::MAX ~ 4_000_000_000
        let mut cursor = buf.len();
        while x > 0 {
            cursor -= 1;
            buf[cursor] = b'0' + (x % 10) as u8;
            x /= 10;
        }
        // return f(
        ::core::str::from_utf8(&buf[cursor ..]) // refers to a local!
            .unwrap()
        // );
    }
}
# #[with]
# fn main ()
# {
#     let s: &'ref str = 42.to_str();
#     assert_eq!(s, "42");
# }
```

Example of a user of the trait (≠ an implementor).

```rust
# use ::with_locals::with; trait ToStr { #[with] fn to_str (self: &'_ Self) -> &'ref str ; }
#
impl<T : ToStr> ::core::fmt::Display for __<T> {
    #[with] // you can #[with]-annotate classic function,
            // in order to get the `let` assignment magic :)
    fn fmt (self: &'_ Self, fmt: &'_ mut ::core::fmt::Formatter<'_>)
      -> ::core::fmt::Result
    {
        //      You can specify the
        //      special lifetime instead of applying `[with]`
        //      vvvv
        let s: &'ref str = self.0.to_str();
        fmt.write_str(s)
    }
}
// (Using a newtype to avoid coherence issues)
struct __<T : ToStr>(T);
```

See [`examples/main.rs`](https://github.com/danielhenrymantilla/with_locals.rs/blob/master/examples/main.rs)
for more detailed examples within a runnable file.

<span id="special-lifetime"></span>

## Usage and the "Special lifetime".

["special lifetime"]: #special-lifetime

Something important to understand _w.r.t._ how `#[with]` operates, is that
sometimes it must perform transformations (such as changing a `foo()` call into
a `with_foo(...)` call), and sometimes it must not; it depends on the semantics
the programmer wants to write (that is, not _all_ function calls rely on CPS!).

Since _a procedural macro only operates on syntax_, it cannot understand such
_semantics_ (_e.g._, it is not possible for a proc-macro to replace `foo()`
with `with_foo()` if, and only if, `foo` does not exist).

Because of that, **the macro expects some syntactic marker / hints that tell it
when (and where!) to work**:

 1. Obviously, the attribute itself needs to have been applied (_on the
    enscoping function_):

    ```rust,ignore
    #[with('special)]
    fn ...
    ```

      - Note: if no override is provided, `#[with]` defaults to
        `#[with('ref)]`.

 1. Then, the macro will inspect to see if **there is a ["special lifetime"]
    within the return type of the function**.

    ```rust,ignore
    //        +-------------+
    //        |             |
    //     --------         V
    #[with('special)] // vvvvvvvv
    fn foo (...)   -> ...'special...
    ```

    That will trigger the transformation of `fn foo` into `fn with_foo`, with
    all the taking-a-callback-parameter shenanigans.

    **Otherwise, it doesn't change the prototype of the function**.

 1. Finally, the macro will also inspect the function body, to perform the
    call-site transformations (_e.g._, `let x = foo(...)` into
    `with_foo(..., |x| { ... })`).

    These transformations are only applied:

      - On the `#[with]`-annotated statements: `[with] let ...`;

      - _Or_, on the statements carrying a type annotation that mentions the
        ["special lifetime"]:

        ```rust,ignore
        let x: ... 'special ... = foo(...);
        ```

### Remarks

  - By default, the ["special lifetime"] is `'ref`. Indeed, since `ref` is a
    Rust keyword, it is not a legal lifetime name, so it is impossible for it
    to conflict with some real lifetime parameter equally named.

  - But `#[with]` allows you to rename that lifetime to one of your liking, by
    providing it as the first parameter of the attribute (the one applied to
    the function, of course):

    ```rust
    use ::core::fmt::Display;
    use ::with_locals::with;

    #[with('local)]
    fn hello (name: &'_ str) -> &'local dyn Display
    {
        &format_args!("Hello {}!", name)
    }
    ```

## Advanced usage

If you are well acquainted with all this CPS / callback style, and would just
like to have some sugar when defining callback-based functions, but do not want
the attribute to mess up with the code inside the function body (_i.e._, if
you want to opt-out of the magic continuation calls at `return` sites _& co._),

  - for instance, because you are interacting with other macros (since they
    lead to opaque code as far as `#[with]` is concerned, making it unable to
    "fix" the code inside, which may lead to uncompilable code),

then, know that you can:

  - directly call the `with_foo(...)` functions with hand-written closures.

    This is kind of obvious given how the functions end up defined, and is
    definitely a possibility that should not be overlooked.

  - and/or you can add a `continuation_name = some_identifier` parameter to the
    `#[with]` attribute to disable the automatic `return continuation(<expr>)`
    transformations;

      - Note that `#[with]` will then provide a `some_identifier!` macro that
        can be used as a shorthand for `return some_identifier(...)`.

        This can be especially neat if the identifier used is, for instance,
        `return_`: you can then write `return_!( value )` where a classic
        function would have written `return value`, and it will correctly
        expand to `return return_(value)` (return the value returned by the
        continuation).

#### Example

```rust
use ::core::fmt::Display;
use ::with_locals::with;

#[with(continuation_name = return_)]
fn display_addr (addr: usize) -> &'ref dyn Display
{
    if addr == 0 {
        return_!( &"NULL" );
    }
    with_hex(addr, |hex| {
        return_(&format_args!("0x{}", hex))
    })
}
// where
#[with]
fn hex (n: usize) -> &'ref dyn Display
{
    &format_args!("{:x}", n)
}
```

## Powerful unsugaring

Since some statements are wrapped inside closures, that basic transformation
alone would make control flow statements such as `return`, `?`, `continue` and
`break` to stop working when located in the scope of a `#[with] let ...`
statement (after it).

```rust,compile_fail
use ::core::fmt::Display;
use ::with_locals::with;

#[with]
fn hex (n: u32) -> &'ref dyn Display
{
    &format_args!("{:#x}", n)
}

fn main ()
{
    for n in 0 .. { // <- `break` cannot refer to this:
        with_hex(n, |s| { // === closure boundary ===
            println!("{}", s);     // ^ Error!
            if n >= 5 {            // |
                break; // ------------+
            }
        })
    }
}
```

And yet, when using the `#[with] let` sugar the above pattern seems to work:

```rust
use ::core::fmt::Display;
use ::with_locals::with;

#[with]
fn hex (n: u32) -> &'ref dyn Display
{
    &format_args!("{:#x}", n)
}

#[with]
fn main ()
{
    for n in 0 .. {
        #[with]
        let s = hex(n);
        println!("{}", s);
        if n >= 5 {
            break;
        }
    };
}
```

  - <details><summary>Click here to see how this is done</summary>

    This is achieved by bundling the expected control flow information within
    the return value of the provided closure:

    ```rust,ignore
    for n in 0 .. {
        enum ControlFlow<T> {
            /// The statements evaluated to a value of type `T`.
            Eval(T),

            /// The statements "called" `break`.
            Break,
        }

        match with_hex(n, |s| ControlFlow::Eval({
            println!("{}", s);
            if n >= 5 {
                return ControlFlow::Break;
            }
        }))
        {
            ControlFlow::Eval(it) => it,
            ControlFlow::Break => break,
        }
    }
    ```

    ___

    </details>

### Debugging / Macro expansion

If, for some reason, you are interested in seeing what's the actual code
generated / emitted by a `#[with]` attribute invocation, then all you have to
do is to enable the `expand-macros` Cargo feature:

```toml
[dependencies]
# ...
with_locals = { version = "...", features = ["expand-macros"] }
```

This will display the emitted code with a style very similar to `cargo-expand`,
but with two added benefits:

  - It does not expand _all_ the macros, just the `#[with]` one. So, if within
    the body of a function there is something like a `println!` call, the
    actual internal formatting logic / machinery will remain hidden and not
    clobber the code.

  - Once the Cargo feature is enabled, a special env var can be used to
    **filter the desired expansions**:

    ```bash
    WITH_LOCALS_DEBUG_FILTER=pattern cargo check
    ```

      - This will then only display the expansions for functions whose name
        _contains_ the given pattern. Note that this does _not_ involve the
        fully qualified name (with the outer modules), it's the bare name only.

  - That being said, this only works when the procedural macro is evaluated,
    and `rustc` will try to cache the result of such invocations. If that's the
    case, all you have to do is perform some dummy change within the involved
    file, and _save_.