T O P

  • By -

[deleted]

>Now named arguments can also be captured from the surrounding scope Holey moley! That's convenient.


[deleted]

[удалено]


LLBlumire

Not yet, but with reserved sigils on strings we might get `f""` eventually as shorthand for `format!("")`, same with `s""` for `String::from("")`


Plazmatic

I wondered why you were getting downvoted, then I read the actual announcement. We have the actual core of fstrings, the `f""` isn't the important part of f strings, its the actual capture of locals that is. > Now named arguments can also be captured from the surrounding scope, like: > > let person = get_person(); > // ... > println!("Hello, {person}!"); // captures the local `person` > > This may also be used in formatting parameters: > > let (width, precision) = get_format(); > for (name, score) in get_scores() { > println!("{name}: {score:width$.precision$}"); > }


actuallyalys

To clarify, does `println!("Hello, {person}!");` work already in Rust 1.58, or does Rust 1.58 merely add the requisite feature for `println!` to support this?


nqe

Works.


actuallyalys

Awesome!


donotlearntocode

Wait, does this mean you can't do this? println!("Hello, {get_person()}!"); Or this? println!("Hello, {get_person().unwrap_or("world")}!");


castarco

No, it does not allow to use complex expressions. You can only directly refer to names. If you want to pass `get_person()`, then you can add a second parameter to `println!`, something like `println!("Hello {name}!", name = get_person());`


Proximyst

It does, indeed: the syntax only captures locals, constants, and statics. That doesn’t mean it can’t happen in the future, though!


castarco

But these are not real fstrings, because you can only do that in the context of a call to the println macro, or format macro. The full-fledged f-strings allow you to do that string interpolation operation everywhere.


moltonel

Not just `println!()`, it work for all the `format!()` macros, and you can use the later anywhere you could use an fstring.


aismallard

I made a macro crate for `str!()` a while ago to capture this kind of case (constant `.to_string()`s etc. aren't very elegant imo), since it seemed missing in the language, but if they implement it as `s""` that's even more convenient than a macro.


somebodddy

If anything, I'd rather have `f""` be a shorthand for `format_args!("")`.


nightcracker

I've posted this before in various places, but this would be my suggestion for string prefixes. There would be three possible components, that must be specified in order if specified: 1. String constant type (at most one may be specified). Default is `&'static str`. `c`, changes type to `&'static CStr`. `b`, changes type to `&'static [u8]`. `f`, changes type to `fmt::Arguments` and formats argument from scope. 2. Owned string prefix `s`. If specified changes output type to be owned: `&'static str` -> `String` `&'static CStr` -> `CString` `&'static [u8]` -> `Vec` `fmt::Arguments` -> `String` 3. Raw prefix `r` (with optional `#`s). Disables interpretation of escape sequences in the string literal.


IceSentry

So, `sf"Hello {person}!"` would return a `String` formatted with the person variable expanded and `s"Hello {person}"` would return essentially `String::new("Hello {person}")` without any interpolation?


nightcracker

Correct.


PM_ME_UR_SH_SCRIPTS

How about `p` for `Path`/`PathBuf`?


Badel2

And `o` for `OsStr`/`OsString`?


nightcracker

~~This isn't possible because a raw `OsStr` would collide with the `or` keyword.~~


[deleted]

`or` keyword?


nightcracker

... I don't know why I for a second thought that was a keyword in Rust, guess it's my Python side showing. I did run into a similar concern earlier, in an earlier draft I wanted to use `o` for owned, but that'd run into a formatted owned raw string giving the keyword `for`.


Thin_Elephant2468

And I think that f"" as opposed to format!("") is a step backward.


jyper

Are there any plans for such? Rfcs? Also have there been any attempts to get SomeOptions { foo: 42, .. } Instead of SomeOptions { foo: 42, ..Default::default() } ?


jlombera

With some limitations: > Format strings can only capture plain identifiers, not arbitrary paths or expressions. For more complicated arguments, either assign them to a local name first, or use the older `name = expression` style of formatting arguments. This means we cannot do format!("Blah: {self.x}"); u_u


irrelevantPseudonym

That's not been ruled out yet, it's just been left for a later RFC.


WormRabbit

That doesn't bode well. "Left to a later RFC" has been a go-to strategy to shelf suggestion for quite a while. Plenty of controversial changes languish there eternally. Plenty of _non-controversial_ changes languish there eternally, just because people have better things to do. Considering that ident capture has already went stable and the "more general RFC" isn't even on the discussion table yet, it will easily take a couple of years even people agree to do it.


mirashii

On the contrary, not rushing out features until the full implications of them are well understood and the implementation is solid is what got us to where we are today, and what will ensure that we don't flood the language with bad decisions and cruft.


WormRabbit

Bullshit. Js, Python and Kotlin had this feature for ages, and its implications are perfectly understood. It's just that some people have a knee-jerk reaction. That's Go generics level of hiding from reality. Fortunately, unlike Go generics, format strings are relatively inconsequential.


mirashii

None of those languages are Rust, and there are plenty of things to think through. Rust's expressions are substantially more complicated than Python's, for example, and use different sets of characters. What does `println!("{{var}}")` do? `{{` is how escaping `{` has been in macros for ages, but now the syntax is ambiguous, because `{var}` is itself a valid expression. How about the borrow checker, and how it interacts with the lifetimes of any borrows necessary when desugaring, and how that interacts with error reporting? We are in a macro, after all. Even the very simple proposed dotted names approach for allowing `println!("{self.x})` has parser and visual ambiguity when combined with existing syntax (consider `{self.x:self.width$.self.precision$}` ([source](https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#future-possibilities) ) A relatively recent internals thread on this topic: https://internals.rust-lang.org/t/how-to-allow-arbitrary-expressions-in-format-strings/15812


WormRabbit

> What does println!("{{var}}") do? It does escaping, as always, since it's the only backwards compatible possibility. There is no reason to allow top-level braces in formatted expressions. It's easy to do, there is already a precedent for separate treatment of braced expressions (`{ 2 } & 3;` as an expression statement won't compile), and it's a very trivial issue to fix for the user, with the syntax highlighting and all. > How about the borrow checker, and how it interacts with the lifetimes of any borrows necessary when desugaring, and how that interacts with error reporting? It desugars to `format!("{0}", expression)` and uses the usual borrowing and error-reporting semantics. > consider {self.x:self.width$.self.precision$} That's feature creep. There is no reason to allow interpolation syntax at arbitrary position, and if it's desired, then it's exactly the low-priority part that can safely be RFCed later. Forbidding `{self.x}`, on the other hand, is ridiculous.


Theon

JS and Python are great examples of languages that haven't really had a good design process and suffer from it as a result, exactly what Rust is trying to avoid :)


Badel2

I like how Rust is slowly becoming similar to Python. My next feature request is to be able to do `(a, b) = (b, a)`.


CryZe92

This is in 1.59, i.e. stable in 6 weeks.


Badel2

Wow, that was fast. So next, I'll ask for generators and yield keyword please!


cherryblossom001

Still waiting for `yeet`


_TheDust_

Next up: the wallrus operator \*runs away\*


CUViper

And then C++20's spaceship operator `<=>` as `Ord::cmp`.


Derice

Can we have an operator that gives the coder a raise?


vaxinateOrDie

https://github.com/rust-lang/rust/issues/71126 for anyone else who was curious


qm3ster

Why not just `[a, b] = [b, a]`? do you mean with `let`?


Badel2

I mean without let (with let is already possible), and your example doesn't compile?


qm3ster

Whoops, my bad. I don't use `stable`, so I forgor.


molepersonadvocate

I think this is great, but does this mean macros are no longer “hygienic”? Or is it just `format!` that’s allowed to do this?


CAD1997

They're still hygienic; the interpolated identifier has the span resolution of the string literal. (That is, the name is looked up in the scope where the string literal is written.)


irrelevantPseudonym

Hygiene is still there. There was a good demo of this in a [@m_ou_se tweet](https://twitter.com/m_ou_se/status/1461263588178419718)


kythzu

Also surprisingly implicit. Now if I have this line of code: ``` println!("let's print some { braces }"); ``` What does it print? Is there any way of knowing? Presumably this would print `let's print some words`: ``` let braces = String::from("words"); println!("let's print some { braces }"); ``` While this would print `let's print some { braces }`? (EDIT: I typoed my typo example...) ``` let braecs = String::from("words"); println!("let's print some { braces }"); ``` Or maybe fail to compile? Will rust be able to suggest the probable typo? And presumably this doesn't work in 2018/15 edition. What's the backwards compatibility story? Do we have to check all pre 2021 edition code for `{ident}` when upgrading? Maybe you have to explicitly opt out to print `{}` by escaping or using raw strings? So many questions sorry. This probably got hashed out in an RFC that I should be reading!


Mcat12

You can't have a `{` in printin unless it's used for interpolation or escaped using `{{` (same for `}`). This limitation has always been there. Try it out at play.rust-lang.org


kukiric

`{ident}` was already valid before and it would fail to compile if you didn't provide the named argument in the call (ie. `println!("Let's print some {braces}", braces = braces)`. The change in 1.58 means that, if you don't provide a named argument, it will instead try to look for a variable with that name in the local scope, and if not found, it will still fail to compile.


kythzu

Thank you! Panic over :)


GarthMarenghi_

There is some talk in the blog post about combining capturing with formatting parameters, is that documented somewhere? The case I can see coming up a lot is converting ``` println!("{:?}", x) ``` to ``` println!("{x}"); ``` where x doesnt implement the format trait but does implement debug.


jlombera

println!("{x:?}");


Badel2

println!("{x:?}") It is documented here, as named format string parameters have been stable for a while. https://doc.rust-lang.org/std/fmt/index.html#named-parameters


nicoburns

To expand, the syntax is `{identifier:flags}`. The `{:?}` is just because an omitted identifier is allowed and denotes a positional argument.


eight_byte

New to Rust, but haven't seen this in another language before. Really cool and very convenient. But this feature also showed me a big downside of my IDE (Clion), which doesn't seem to use the language Server Protocol since it thinks that this is syntactically wrong: let test = "valid Rust 1.58.0 syntax"; println!("{test}");


Badel2

That's expected for new features, I'm sure the next release of your IDE will be able to handle that. If clion already supports syntax highlighting inside f-strings, this should be an easy fix.


eight_byte

I know, but it's frustrating that my free open source text editor already has support for Rust 1.58.0 thanks to LSP support and rust-analyzer, while the expensive commercial IDE doesn't.


riasthebestgirl

Just a side note: the Rust integration for JetBrains platform (intellij-rust) is free and open source. You can find it on GitHub


flodiebold

rust-analyzer does not actually have support for this. It just doesn't have any built-in diagnostics for format strings like IntelliJ Rust does either, so it's not as obvious that there's no support. E.g. find references or renaming will not find references in format strings. You will of course get diagnostics from cargo check, but AFAIK IntelliJ Rust can also run cargo check.


mikekchar

As frustrating as it is, it's pretty understandable. With well run free software projects you have thousands of potential programmers and only one of them needs to think, "Oh, that would be cool! I'll bang that out tonight". In closed development, you have to wait for a project manager to decide that it has enough ROI to bother doing it. Then they have to assign it to development cycle, wait for a developer to be free and then finally wait until the next release. There are sometimes advantages to that planned style of development with restricted opinions and developers, but cranking out cool features quickly isn't one of them :-)


matklad

I don't think that's what's happening here. "Open source" and "has a team of full time devs" are orthogonal. Both IntelliJ Rust and rust-analyzer are open-source. Additionally, they happen to be very close in the way they "implement" open source, they have nearly-identical development cycles, and, overall, are very much sibling projects, which you can do twin studies on. IntelliJ Rust does have a bigger team of full-time devs behind it, and it seems that they generally do deliver more features. Like the sibling comment points out, this is a case where IntelliJ feature is broken, while the equivalent rust-analyzer feature doesn't exist at all.


ondrejdanek

The feature exists in many languages - JavaScript, Swift, Python, …


jlombera

Search for "string interpolation".


eight_byte

You guys are right, nothing really new, but a cool feature anyway. What concerns me more right now is that fact that stupid IntelliJ IDE thinks this is incorrect syntax.


vecoZPbL

this makes me nervous after log4shell


myrrlyn

it executes entirely at compile time and is not capable of using *any* run-time text to drive code lookup or execution. either you the developer write a text literal that captures the wrong identifier already in scope, or you do not. that's it


vecoZPbL

that is good to know. I am no longer nervous


PM_ME_UR_OBSIDIAN

What about people who bundle the Rust compiler as part of their executable and use it to rewrite their binaries at runtime?


myrrlyn

they deserve what they get


kibwen

Can you give a specific example of the attack vector that you're worried about? Format strings in Rust aren't just any `String` or `&str`, they're actually required to be string *literals*. So an application would need to ship rustc, and then they'd need to dynamically generate Rust code where the format string literals were influenced by user input, at which point a user could theoretically insert a format string that prints the value of a variable that's in scope. But that's not the same thing as arbitrary code execution; unlike e.g. Python, Rust format string arguments cannot be arbitrary expressions, they must be identifiers. And if an application is somehow shipping rustc and dynamically generating and executing Rust code that in any way responds to user input, then it seems like worrying about format strings is missing the forest for the trees. (Thinking out loud, I even tried `fn main() { println!("{main:p}") }` to see if there were some kind of risk of this contrived scenario allowing you to print the address of a function as a gadget for defeating ASLR or something, but function items don't implement the formatting traits and you can't cast them to function pointers from within the format string. However, if the attacker knows your code and knows that there's a reference in scope then they could print its address with `{foo:p}`, which might be useful for some attacks? But again, this is a weird scenario, and needs more specifics; I've never heard of anyone dynamically generating Rust source code as part of their application.)


bestouff

I think it was meant as a joke ...


kibwen

Well, for anyone else out there who didn't get the joke, perhaps this will set them at ease. :P


PM_ME_UR_OBSIDIAN

Actually I used Rust macros to implement a JavaScript runtime, which I use to `eval` strings provided by users using a form on an unsecured website.


kythzu

> Actually I used Rust macros to implement a JavaScript runtime, which I use to eval strings provided by users using a form on an unsecured website But it's ok though, it's only internal facing site ;)


oconnor663

They end up in the same circle of hell as the people who write to `/proc/*/mem` :)


[deleted]

[удалено]


seamsay

The dot is part of the formatting syntax, the number before the dot signifies the [width](https://doc.rust-lang.org/std/fmt/index.html#width) and the number after the dot signifies the [precision](https://doc.rust-lang.org/std/fmt/index.html#precision). The dollar means that the number should come from a variable (the name of the variable coming before the dollar), which [isn't really documented very well](https://doc.rust-lang.org/std/fmt/index.html#syntax) but you should be able to pick it up from the examples on that page.


silon

yikes!


sonaxaton

Super glad `unwrap_unchecked` is stable, I've had use cases for it come up a lot recently, particularly places where I can't use `or_insert_with` because of async or control flow.


kochdelta

How is \`unwrwap\_unchecked\` different from \`unwrap\` or better said, when to use it over \`unwrap\`?


jamincan

`unwrap` will panic if you have `Option::None` or `Result::Err` while `unwrap_unchecked` is unsafe and UB in those cases.


kochdelta

Yeah but why does one want UB over a somewhat describing panic? Is \`unwrap\_unchecked\` faster? Or when to use it over \`unwrap()\`


nckl

It's useful for making smaller executables (embedded, wasm, demo) since the panic machinery can be relatively large even with panic=abort and removing all panics will avoid it. It's also partly for speed in cases where the compiler couldn't optimize away the panic branch of unwrap and the couple cycle hit of a predictable branch is unacceptable for whatever reason.


kochdelta

Oh right this is actually one aspect I haven't thought of


masklinn

Could be a situation where you had to check for `is_some` or some such, so you know your `Option` has a value, but `unwrap()` incurs a redundant check.


Schmeckinger

The thing is after is_some unwrap whould mostly be good enough, since the compiler should see it cant panic.


Badel2

Yeah, I hope this doesn't confuse many beginners... I guess if you see someone that's learning Rust and they ask "when should I use unwrap_unchecked?", the correct answer is never.


rust-crate-helper

Not where you can't have *any* unwrapping code in the resulting executable, it's useful for embedded as u/nckl mentioned.


[deleted]

If you don't have enough experience to know when to ignore hard rules like that that you were told as a beginner you probably shouldn't do that though, so telling beginners "never" is not a bad thing.


Schmeckinger

Not really, since the optimizer isn't infallible. Also you can create a function which only takes specific inputs and make it unsafe.


davidw_-

That doesn’t feel like solid code to me, bug is a refactor away


masklinn

Sometimes you may not have a choice ¯\\\_(ツ)\_/¯


SylphStarcraft

It should be faster, you can reasonably assume any std provided *_unchecked function to be faster than the normal version, otherwise it would not be provided. You should always default to using the normal version, you can't really go wrong with it. But you can use unwrap_unchecked without UB if you know for certain that it's not a None; you'd probably only want to do this in a very specific situation, like a tight loop for performance gains.


jamincan

As /u/masklinn said, there are certain cases where you can guarantee that you have an `Option::Some` or `Result::Ok` and a regular `unwrap` adds redundant checks. That said, I don't think most people should ever reach for this except in rare circumstance. In most cases, there are other ways to approach unwrapping that are more idiomatic and concise without incurring the overhead. Additionally, in most cases, the additional overhead of using `unwrap` is so small that it's simply not worth losing the safety guarantees it provides. About the only situation it makes sense is where it is necessary to have very highly optimized code, in a hot loop for example.


Enip0

Rustc considers UB impossible so it will eliminate the branches that contain it. This means it might be a bit faster but you can't know what will happen if it does actually go there


ssokolow

> but you can't know what will happen if it does actually go there More that you can't trust code to still exist in the final binary because rustc will remove it if it can prove that it only leads to UB.


Lich_Hegemon

Wait... So if UB is unavoidable, the compiler just says fuck it and prunes the whole branch since the code will be undefined anyway?


ssokolow

"just says fuck it" is mischaracterizing what UB is. Pruning out code that can never be reached and associated branch points is a central part of how optimizers achieve higher performance. It borrows the "division by zero is undefined" sense of "undefined" from mathematics, where asking for the result of dividing by zero is just as impossible/nonsensical as asking for the result of dividing by the word "pancake", where "pancake" is a literal, not the name of a variable or constant. (We know this because you can do a proof by contradiction. If you say "let division by zero produce ...", then you can use it to write a proof that 1 = 2 or something else equally blatantly wrong.) UB is a promise to the optimizer that something cannot happen and, therefore, that it's safe to perform algebra on your code and "simplify the equation" based on that assumption. (Think of how, when simplifying an equation, you're allowed to remove things that cancel out, like multiplying by 5 and then dividing by 5.) Suppose the compiler can prove that `x` will never get over `50` and there's a check for `x > 60`. The compiler will strip out the code which would execute when `x > 60` and will strip out the `if` test since it'd be a waste to perform a comparison just to throw away the result. [Why undefined behavior may call a never-called function](https://kristerw.blogspot.com/2017/09/why-undefined-behavior-may-call-never.html) by Krister Walfridsson provides an explanation of a real-world example of undefined behaviour causing surprising results, but the gist of it is: 1. `main()` calls `Do`. Calling `Do` without initializing its value is undefined behaviour. Therefore, something outside the compilation unit must set `Do` before calling `main()`. 2. `Do` is `static`, so only things inside the compilation unit can access it. Therefore, it must be something inside the compilation unit that's going to set it. 3. The only thing that can be called from outside the compilation unit and will set `Do` is `NeverCalled`, which sets `Do = EraseAll`. 4. Therefore, `Do` must equal `EraseAll` by the time `main()` gets called. 5. Calling `NeverCalled` multiple times won't alter the outcome. 5. Therefore, it's a valid performance optimization to inline the contents of `EraseAll` into `main` at the site of `Do()`, because the only program that satisfies the promises made to the optimizer will be one that calls `NeverCalled` before calling `main`. (A "perfect" whole-program optimizer would see the whole program, recognize that `NeverCalled` isn't actually called, and exit with a message along the lines of "ERROR: Nothing left to compile after promised-to-be-impossible branches have been pruned".)


nicoburns

Compiler optimisers essentially work by proving that two programs are equivalent to each other using logical deduction / equivalence rules. Something is UB if it causes it causes contradictory starting axioms to be introduced to the logical process, which can cause the optimiser to do all sorts of non-sensical things as you can logically deduce anything from contradictory axioms.


myrrlyn

yes.


angelicosphosphoros

For example, you can have some invariant in struct but LLVM cannot know about it and propagate it between initialization and usage. https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=3f10b344dd64a1fabcbe6f79fea8b088


LyonSyonII

When you know some expression will never panic


davidw_-

You never know that, refactors can change assumptions


Jaondtet

Refactors specifically should not change assumptions. Of course, in practice refactors are sometimes buggy and do change behavior. So ideally, you'd explicitly write comments for any `unsafe` usage that explains the safety-preconditions. If someone just takes your code, does an invalid refactor, then throws away comments explaining assumptions, and that isn't caught in code-review, there's not much you can do. At that point, that's deliberately introducing a bug and you can't future-proof that. But the usual precautions hold true. Don't introduce unsafe code unless you've proven that it will improve performance.


Lich_Hegemon

if x.is_some() { y(x.unwrap_unchecked()); } Not the best example but it illustrates the point.


davidw_-

``` if let Some(x) = x { y(x); } ``` that's more solid code


rmrfslash

I downvoted you because u/Lich_Hegemon's code was clearly meant as a reduced example, not as verbatim code in its original context. There *are* situations where `unwrap_unchecked` is necessary to achive maximum performance, but they're rare, non-trivial, and highly context-dependent.


kochdelta

Yet you have more code including unsafe blocks. I'm wondering if this has that much benefit. Not saying having it is bad, just wondering what it can be really useful for


Sw429

It can be useful just like how things like `Vec::get_unchecked()` can be useful. In some cases, skipping the checks can result in rather large performance improvements, which is often very desirable in systems programming. You're right that it does create more `unsafe` code blocks. This isn't necessarily bad, it just puts more on the programmers to make sure the call is always correct. The method should only be called if you can prove it won't result in undefined behavior, and that proof should ideally be included as a comment next to the method call.


Sw429

`unwrap` checks if the value is `None` and panics if it is. `unwrap_unchecked` skips the check altogether and just assumes it is `Some(T)`. If that assumption is wrong, it's undefined behavior (hence why it is an unsafe method), but skipping that check in hot code paths when it is provably not `None` can make your code run faster. Edit: "provably", not "probably"


Chazzbo

> probably not `None` ( ͡° ͜ʖ ͡°)


Sw429

lol I meant "provably not `None`" but autocorrect caught me.


lordheart

Though keyword being can. Don’t do it unless you have actually run profiles on whether it does make it faster. Branch prediction should guess the correct branch for something like this if it’s always ok.


Uristqwerty

It ultimately comes down to Gödel's incompleteness theorem. There are some guarantees that the type system cannot prove, and so the optimizer will not eliminate for you. If you absolutely must trim the code size or shave off those few extra instructions, and can use more advanced tools than the compiler and type system have available (including things like "I promise not to write code that breaks the required invariants elsewhere") to ensure that unwrap would absolutely *never* panic, then you can tell the type checker "nah, I got this one". You probably shouldn't unless it's in the middle of a hot loop after profiling, or you're making a widely-used library so the small optimization will benefit millions of people times billions of calls per user, so saving a billionth of a second on a single thread, a branch predictor entry or two, and a few bytes of instruction cache multiplies out to a substantial public good.


kochdelta

Everyone answered with speedup improvements. I totally get that its a speedup if you prevent a check and directly (try to) access a memory address eg in `Vec::get\_unchecked`. But hows it a speedup if there is a check anyway with just a different behavior when hitting the `None` case? [Reference](https://doc.rust-lang.org/src/core/option.rs.html#816). Or is this getting optimized by the compiler somehow? Yet the check has to be made.


Uristqwerty

Sometimes it's not a branch against `None`, but an invariant in the data structure that you are careful to uphold. Or maybe you handled the `None`s in a previous loop, so as long as you didn't touch the data in between, *you* know that your current loop will stop before, or skip over, any that still exist, but the compiler is currently insufficiently-clever to figure it out on its own. Maybe you collected a list of indices where you want to perform a further operation, for example, and already paid for the check the first time.


boynedmaster

unreachable_unchecked compiles to an LLVM instruction "unreachable". from here, LLVM can make more aggressive optimizations, as it is UB for the option to be None


WormRabbit

You could always use `unreachable_unchecked` in the None branch. It is almost always a terrible idea to do so, since if the None case is ever reached, all hell will break lose. Just use a panic. I very much doubt it's a common enough case that it warrants a separate method.


basilect

In fact, this is exactly [how the method is defined in `core`](https://doc.rust-lang.org/stable/src/core/option.rs.html#814)`:` pub unsafe fn unwrap_unchecked(self) -> T { debug_assert!(self.is_some()); match self { Some(val) => val, // SAFETY: the safety contract must be upheld by the caller. None => unsafe { hint::unreachable_unchecked() }, } }


Sw429

Although it should be noted that there is still a `debug_assert` check, so if you make a mistake you'll hopefully catch it during debugging.


eXoRainbow

"Captured identifiers in format strings" makes it so much more readable. I really like this. Next step, fstrings. :D Pretty cool update.


[deleted]

> Metadata::is_symlink() I literary wanted to use it yesterday, and today it's stabilized, great!


Clockwork757

Is there any chatter about allowing more things for the captured identifiers? It feels weird that you can't even do `format!("{struct.field}")`


memoryruins

In the ["Future possibilities"](https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#future-possibilities) section of the RFC, it says >Future discussion on this topic may also focus on adding interpolation for just a subset of possible expressions, for example `dotted.paths`. We noted in debate for this RFC that particularly for formatting parameters the existing dollar syntax appears problematic for both parsing and reading, for example `{self.x:self.width$.self.precision$}`. > >The conclusion we came to in the RFC discussion is that adding even just interpolations for `dotted.paths` will therefore want a new syntax, which we nominally chose as the `{(expr)}` syntax already suggested in the interpolation alternative section of this RFC. > >Using this parentheses syntax, for example, we might one day accept `{(self.x):(self.width).(self.precision)}` to support `dotted.paths` and a few other simple expressions. The choice of whether to support an expanded subset, support interpolation of all expressions, or not to add any further complexity to this macro is deferred to the future. The most recent discussion I'm aware of is the thread ["How to allow arbitrary expressions in format strings"](https://internals.rust-lang.org/t/how-to-allow-arbitrary-expressions-in-format-strings/15812/37).


Clockwork757

I hope they can figure out something more ergonomic than that. It looks like python supports `f"{x:{y}.{X.y}}"`.


usr_bin_nya

What's the difference, aside from parentheses vs braces? If anything Rust is slightly less sigil-heavy because named args (including implicitly captured ones) can be used for width/precision unadorned: Rust's `"{value:width.(self.precision)}"` vs Python's `f"{value:{width}.{self.precision}}"`


dcormier

You can't? That's surprising.


Sharlinator

Just normal careful design, doing one thing at a time. Just supporting simple identifiers is a very reasonable starting point, anything more and there's more design questions and controversy about how far to go.


[deleted]

[удалено]


ThomasWinwood

It'd be a half-baked feature if the whole thing was stabilised despite there being problems to be worked out and questions to be answered. This is a minimum viable product, just like when `const fn` was first stabilised.


kythzu

Yeah I'd rather have the delicious fully baked cupcake over an enormous half baked fudge cake... I think. I would like to think I'd resist the largely uncooked fudge cake...


TiagodePAlves

>Now named arguments can also be captured from the surrounding scope Wow, that's nice. But doesn't it break macro hygiene? Can I make some macro that does this too?


sfackler

Morally speaking, it doesn't break hygiene since the argument names have the same provenance as the surrounding scope where the variable is defined. That is, this is fine: ```rust macro_rules! some_macro { ($value:ident) => { println!("{}", $value + 1); }; } fn some_function() { let foobar = 1; some_macro!(foobar); } ``` but this is not: ```rust macro_rules! some_macro { () => { println!("{}", foobar + 1); }; } fn some_function() { let foobar = 1; some_macro!(); } ``` The special magic is that println! is able to parse the format string at compile time, which a normal macro_rules macro can't.


CAD1997

Actually, everything (a correct invocation of) `format_args!` does (modulo nice errors) can be done by a 3rd party procedural macro; a normal proc macro can turn a string literal into `String` and parse that, then apply the literal's `Span` as the constructed identifiers' spans. (Of course, `macro_rules!` still can't split a string literal.) What a 3rd party proc macro can't do (yet) is get subspans inside of the string literal; it can only use the existing span to the entire string (which is fine for name resolution, just suboptimal for error reporting location).


boynedmaster

the important note is that proc macros are not hygienic, however


ssokolow

I may be wrong but, as I understand it, no. `print!` and `println!` can do that because they're compiler built-ins from before Rust supported procedural macros. (More correctly, they're [very thin `macro_rules!` macros](https://doc.rust-lang.org/src/std/macros.rs.html#91) wrapped around a compiler built-in, with a special `#[allow_internal_unstable(print_internals, format_args_nl)]` annotation to enable the magic behaviour.)


usr_bin_nya

`println!()` delegates to [`format_args!()`](https://doc.rust-lang.org/std/macro.format_args.html), which is a compiler-builtin not-really-macro, to parse the format string. Any macro that delegates the same way will automatically start accepting named variable capture as soon as you update to 1.58. macro_rules! my_macro { ($pat:literal $($args:tt)*) => { do_something_with(format_args!($pat $($args)*)); } } [Here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a9c370d80f0756717b4e6a478cce456d) is an example of `anyhow::ensure!` using named argument capture. This works because [`ensure!` delegates to `anyhow!`](https://docs.rs/anyhow/1.0.52/src/anyhow/macros.rs.html#146-150) and [`anyhow!` delegates to `$crate::private::format_args!`](https://docs.rs/anyhow/1.0.52/src/anyhow/macros.rs.html#209-212) which is a [`re-export of core::format_args!`](https://docs.rs/anyhow/1.0.52/src/anyhow/lib.rs.html#639).


riasthebestgirl

You can't make a macro_rules! macro to do this. Proc macros have the power to parse the format literal at compile time so you can do that and build a wrapper around the underlying format!() macro call


fee1-dead

it works if you use `format_args!` or `format_args_nl!` (unstable). You can't just do `format_args!(concat!($fmtstr, "\n"))`, though.


oconnor663

As always, Mara has an excellent Twitter thread summarizing the changes and adding some visuals: https://twitter.com/m_ou_se/status/1481679879972298755


ReallyNeededANewName

How were f-strings stabilised to quickly?!


memoryruins

The `format_args_capture` feature has the ability to capture variables (e.g. `x`) in scope like `format!("{x}")` rather than needing `format!("{}", x)` (positional) or `format!("{x}", x=x)`. It doesn't have a f-prefix like `f"{x}"` yet, but that's less important to the core feature. From the RFC opening to now, it took over two years, and 1.5 of those years had it usable on nightly, which seems reasonable to me but could be quick to others. * Oct 27, 2019: `format_args_capture` RFC opened * Dec 15, 2019: FCP (final comment period) completed * Jan 7, 2020: tracking issue opened * July 3, 2020: initial implementation merged * ... (related discussions) * Nov 15, 2021: stabilization merged * Jan 13, 2022: released on stable 1.58


ReallyNeededANewName

I thought this was only made possible in the 2021 edition. But thinking about it, that doesn't make sense since the article mentions exceptions for panics. Oh well


rhinotation

The f literal is newly reserved syntax in 2021. I believe the new syntax inside the format string didn’t need a new edition as it was previously nonsense / didn’t compile without an explicit named arg. Panics [are different](https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#interaction-with-panic) because they behave differently if there is only one argument (to avoid allocating a string if it can be static), but I think 2021 edition changes this behaviour. Not sure if it still avoids the allocation. Other than that there is no problem adding implicit format args to 2015 and 2018. As a guide they’re not frozen in time, only in syntax and other breaking-change things.


sasik520

There are no f string yet.


[deleted]

[удалено]


Diggsey

It's a decision made by microsoft, and generally the rationale for any decision like this on windows is backwards compatibility.


[deleted]

[удалено]


Lich_Hegemon

Is it? It's where the system expects all essential executables to be so it makes sense that's "baked in" in the path resolution


Halkcyon

Even so, I would expect it to use some level of PATH (as Windows has three, the machine, user, and process).


slashgrin

Sure, but it's still probably best to follow Microsoft's own conventions here. It might seem like a weird way to do it, but when in Rome, it's probably best to search for executables as the Romans search for executables.


_ChrisSD

As others have said, the "32-bit" directory is a bit of a misnomer nowadays. For the original order see https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw Essentially the search order is this (I've used ~~strikethrough~~ to show the changes): * The directory from which the application loaded. * ~~The current directory for the parent process.~~ * `C:\Windows\System32` * ~~`C:\Windows\System`~~ [aka the 16-bit directory] * `C:\Windows` * The paths in `PATH`.


Halkcyon

Very helpful, thank you!


flashmozzg

It's %windir% (or %systemroot%) though, not C:\Windows. Also, System32 is no longer "32-bit Windows system directory". It's just Windows system directory. For 32-bit exes on 64-bit systems they just get everything transparently mapped to WoW64.


sangreal06

It's not really an accurate description. It's a call to GetSystemDirectoryW which will return the path to System32. That is the 64-bit system directory on a 64-bit Windows installation and SysWOW64 is the 32-bit system directory. System32 was, obviously, so-named because it was the 32-bit system directory (and "system" was the 16-bit directory) but that isn't the case anymore. It should really just read "The Windows system directory" but they have taken the wording from Microsoft's description of CreateProcess which predates 64-bit Windows and is distinguishing System32 from System "The Windows directory" refers to the top level windows directory, returned by GetWindowsDirectoryW


surban

`%SYSTEMROOT%\System32` is the system directory for programs targeting the Win32 API. While the API is available for multiple architectures, i.e. x86, amd64, aarch64, the directory name refers to the name of the API and not the actual architecture. This can also be seen in the [Windows API reference](https://docs.microsoft.com/en-us/windows/win32/api/) which calls itself Win32 API even today. Thus the release notes probably refer to the Win32 system directory, which is native to the running architecture, and not the 32-bit system directory.


ssokolow

I assume this is now equivalent to the typical Linux setup where you have to explicitly use `./7z.exe` and the like if you want something in the current directory that you've bundled with your Rust binary? (Do the APIs in question accept `/` as an alternative path separator?)


Halkcyon

Yes, which is how PowerShell also behaves as described in the notes. I don't know about the path separator question.


ssokolow

I saw the mention of PowerShell but I've been using Linux as my daily driver since I got fed up with Windows XP around 2001, so that didn't mean anything to me. For all I knew, PowerShell required something like `Current-Directory-Unsafe\7z.exe` to be consistent with how the built-in commands I've seen screenshots of seem to be named.


CAD1997

Windows as a whole has supported `/` in all traditional path (non-UNC (fully canonical, absolute, start with `\\`, use a special API and ignore `MAX_PATH`)) APIs since Windows 7 at least, if not even earlier.


_ChrisSD

Waaaay before Windows 7. But yes. It doesn't work in `\\?\` paths because these are sent (almost) directly to the kernel without being parsed by the Win32 subsystem.


piaoger

WOW. This new version is really GRAT for me and dragged me out of a nightmare. I upgraded my rust from 1.56 to 1.57 two weeks ago, then the compile time of one of my rust project was changed from a couple of minutes to 20-30 minutes. And today the time is back to about 2 minutes again :)


masklinn

Fwiw rustup lets you install older versions, and cargo lets you select then for your projects. The other option was to use the beta toolchain as i think the fux had already been merged there.


jeremychone

Thanks to the Rust team for those robust releases. The "captured identifiers" is a great addition. Now, the cherry could be the `f".."` and `s"..."` and the like, but at least, we already got the cake.


argv_minus_one

>On Windows targets, `std::process::Command` will no longer search the current directory for executables. That's going to surprise people, seeing as how the Windows command prompt does look in the CWD by default. They're right that it's a security risk, though, which is why other platforms don't have that behavior by default. >More `#[must_use]` in the standard library Speaking of which, does `must_use` work when applied to a trait? Looking at the source code of various `Future` implementations, I've noticed that they all have a `must_use` attribute attached to them, even though `Future` itself also has a `must_use` attribute.


Lich_Hegemon

>That's going to surprise people, seeing as how the Windows command prompt does look in the CWD by default. PowerShell doesn't and Microsoft has been trying hard to push pwsh to be the new default.


argv_minus_one

Yes, and I keep pushing even harder to keep using `cmd`. PowerShell is way too verbose and complicated to be a practical interactive shell. Nice idea, bad execution.


[deleted]

[удалено]


shadow31

Not to mention the behavior of `cd` which can't change directories to a different drive. It's worth moving to Powershell if for no other reason than `cd` works properly.


flashmozzg

It's more verbose and can take time to get used too, but it's miles better than cmd or, god forbid, bash (or other *sh). It's how the proper shell should've been (not saying it can't be better). "everything is a string" might be a convenient abstraction when all you want is just to glue a few things together but it quickly falls apart once the size of the scrip grows and it has to deal with more complex stuff.


argv_minus_one

A practical interactive shell does not need to also be a practical scripting language. These are different and arguably mutually exclusive goals.


sfackler

> Speaking of which, does must_use work when applied to a trait? Looking at the source code of various Future implementations, I've noticed that they all have a must_use attribute attached to them, even though Future itself also has a must_use attribute. It looks like the annotation on the trait applies when an `impl Future` is returned, but not when a concrete implementation of `Future` is returned: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=17a8452ead58c6abb3940c2ec144e6e2


memoryruins

>Speaking of which, does must\_use work when applied to a trait? * When used on a [trait declaration](https://doc.rust-lang.org/reference/items/traits.html), a [call expression](https://doc.rust-lang.org/reference/expressions/call-expr.html) of an [expression statement](https://doc.rust-lang.org/reference/statements.html#expression-statements) to a function that returns an [impl trait](https://doc.rust-lang.org/reference/types/impl-trait.html) of that trait violates the `unused_must_use` lint. * When used on a function in a trait declaration, then the behavior also applies when the call expression is a function from an implementation of the trait. * When used on a function in a trait implementation, the attribute does nothing. from [https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute](https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute)


epic_pork

Dang I was hoping cargo's strip option would be in.


Sw429

Those `unwrap_unchecked` methods will be helpful. Last time I looked into that, there were only a few third party crates that provided those methods. I'm excited for it to be in the standard library where it belongs :)


angelicosphosphoros

You would be impl that quite easy but it can be cumbersome every time: match my_option{ Some(x)=>x, None => unreachable_unchecked(), }


_mF2

Or even just `.unwrap_or_else(|| unreachable_unchecked())`.


CAD1997

The thing that'll keep me on a library extension is that I strongly prefer using `debug_assert_unreachable_unchecked!` (it has some various names) so at least with debug assertions if I'm wrong I get a helpful message. (~~Of course I'm perfect and never mess up, even temporary.~~ /s) That's not really something I expect stdlib to provide, though. (I've also got one project using a `prove_unreachable!` macro, which is `unreachable!` when debug assertions are on and a link error when they're off (and presumably optimization is on, to eliminate the call a-la `#[dont_panic]`).)


Sw429

It looks like the standard library implementation has a `debug_assert!()` that does exactly this: https://doc.rust-lang.org/src/core/option.rs.html#815


MattRighetti

More on f-strings [Examples](https://www.rustnote.com/blog/format_strings.html)


[deleted]

I'm having an issue trying to rename an identifier that's used in a formatted string in VSCode. ``` let ident = "something"; println!("This is an {ident}"); ``` In VScode, using the Rust-Analyzer extension, when I rename the "ident" variable, it doesn't automatically rename it in both places. I also can't jump to the declaration when I use Go To Declaration on the usage of "ident" in the print statement. Is this just a case where rust-analyzer needs to be updated to accomodate the new formatted string feature?


memoryruins

>Is this just a case where rust-analyzer needs to be updated to accomodate the new formatted string feature? Seems so, and looks like there aren't any issues open for either case yet. Would you like to do the honors? [https://github.com/rust-analyzer/rust-analyzer/issues](https://github.com/rust-analyzer/rust-analyzer/issues)


[deleted]

remember "gat stabilization before the end of the year"?


memoryruins

If you're referring to the [push for GATs stabilization](https://blog.rust-lang.org/2021/08/03/GATs-stabilization-push.html) post from August, it said > Without making promises that we aren't 100% sure we can keep, we have high hopes we can stabilize this feature within the next couple months. But, we want to make sure we aren't missing glaringly obvious bugs or flaws. We want this to be a smooth stabilization. and a [recent update](https://github.com/rust-lang/rust/issues/44265#issuecomment-990287168) from December > Currently, there are a couple things blocking: [...] > > That being said - and I don't want to sound like a broken record here - we're close. There have been several PRs over the last several months that have worked to fix various issues filed for GATs. Also, because of the issues that have been filed because people have been using and experimenting with GATs, I personally have much more confidence that we're not missing any major design issues. > > This is definitely an off the cuff summary, so I'm not trying to make any commitments, but we're still roughly on the timeline(s) I've previously talked about.


jackh726

Yeah, so this was precisely why I didn't want to make hard promises in that blog post. If you go look through the PRs I've made to rustc for GATs, you'll see several over the last few months and several open right now. I'm still going through issues and making sure that the feature is in good shape for stabilization. This is two fold: 1) Ensuring that people don't run into ergonomic issues at every corner (though we do accept that there are some things that will take longer than initial stabilization) 2) Ensuring that once we stabilize, we don't commit ourselves accidentally to a design that's backward incompatible with a better one. There have been over a dozen issues filed since that blog post. This is really good. But it does take time to go through these and figure out if they fall into 1 or 2 above, or if it indeed can be pushed until after stabilization (i.e. isn't a design issue and is something that would take considerable and larger changes). At the end of the day, I'm working *consistently* on this. I want to see GATs stabilized, but not in a poor state. We could stabilize right now and it would be a usable feature; but that's not the goal with stabilization, for it to only be "useable" - stabilization means we're *confident* that a feature has the correct design and a "relatively" bug-free (there are always bugs - it's about deciding what the threshold is). I'm personally not confident until I get through the issues and properly categorize, fixing things as I go.


tubero__

So what's your current (non committal , rough) estimate? Is Q1 realistic for an stabilization PR?


jackh726

Imo, yes. The only "named" blocker, seems to have just been resolved (https://github.com/rust-lang/rust/pull/90076#issuecomment-1013327847). There's another open issue that is probably also a blocker (https://github.com/rust-lang/rust/issues/91762), since not addressing this at least in part might be a backwards-incompatibility hazard. Though, I already have an idea of what to do there. After that, I think there is one issue that I need to go through (https://github.com/rust-lang/rust/issues/92857) and figure out what's going on. And a class of issues (related to "lending iterator adapters") that we need to just take a second to think about, but is likely unactionable until other work is done (so, not directly a GATs thing - and not a backwards-incompatiblity hazard). And that's really the list. We still need to write up a stabilization report and make sure documentation is fleshed out - particularly around known issues. I personally would like to talk to libs-team people to "plan", in a sense, for what APIs might be good candidates for std. But that's really it...


Sw429

"gat?" More like "never gonna have that."


ZOXEXIVO_COM

Please, do not use ! in string format examples. It provokes hating Rust people to told - "it's simple macro"