Rust 1.42
blog.rust-lang.org
blog.rust-lang.org
what do you mean ? in which case don't you get a backtrace in C++ ?
If you're taking first year programming and they give you a compiler incantation that doesn't include -g that will be your experience.
[profile.release]
debug = true
to make this work? $ cat test.c
int g() {
int *p = 0;
*p = 5;
}
int f() {
g();
}
int main() {
f();
}
$ cc test.c
$ ./a.out
[1] 3377 segmentation fault (core dumped) ./a.out
$ gdb -q a.out core
Reading symbols from a.out...
(No debugging symbols found in a.out)
[New LWP 3377]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000557379b7613d in g ()
(gdb) bt
#0 0x0000557379b7613d in g ()
#1 0x0000557379b76158 in f ()
#2 0x0000557379b7616d in main ()Another wrinkle is that the symbol names of extern functions need to be in the binary regardless of debug level, though the main binary is a special case, unlike shared libraries. So you'll get different results again if you add -fPIC -rdynamic (or possibly just -fPIC) to -O3, similar results as-if you compiled a library with -O3 -fPIC -shared. But then if you define f and g as statically scoped -fPIC -rdynamic won't change the behavior.
It follows that there's a potential conflict between code optimization and the ability to determine function names and line numbers for a trace. To preserve the ability to associate the programer counter (PC) to distinct function names and line numbers a compiler might need to abstain from completely inlining, merging, eliding, or otherwise optimizing some functions and code blocks (though that doesn't mean it needs to preserve actual function call behavior). People will endlessly debate the cost+benefit, but it's something to keep in mind for performance critical code blocks.
uh... no. given
volatile char* foo = 0x0;
struct crasher
{
void crash()
{
*foo = 1;
}
};
int main()
{
crasher c;
c.crash();
}
and $ g++ foo.cpp
$ ./a.out
then $ coredumpctl gdb
gives me (gdb) bt
#0 0x000055cea1ff6187 in crasher::crash() ()
#1 0x000055cea1ff615c in main ()
The only case when you wouldn't get function names is if your binary has been stripped manually (or as part of a build system step)Gdb beyond what the learning person can comfortably handle at the time and that's what matters.
New programmers would be using IDEs, press the green arrow "play" button and launch the app with the debugger enabled without them knowing, which would cause the editor to jump directly to where the issue is
If they were teaching you rust be sure that they wouldn't have told you about cargo either.
I'm a self taught C/C++ programmer, learning and becoming comfortable with gdb was one of the first few things I taught myself after learning how to compile code into a binary back in the 1990's.
The Visual Studio Debugger is simply the best. Okay, it's not a powerhouse like WinDBG, or have the knowledge of IDAPro, and various other cool debuggers (OllyDbg, others..), but it's the easiest one to learn (IMHO), and teach. Few bits and tricks, press F5 and you are done :)
[0] https://bithavoc.io/blog/2020/01/01/go-binary-size-descision...
What Rust _should_ do is gradually increase the noisiness of warnings about using this. Eventually it'll disappear from the crate and end-user ecosystem (possibly with a small effort to tidy up abandoned crates).
Java's "never remove a deprecated API" was definitely one of their big mistakes. Even a 10-year deprecation cycle is better than none.
I think this is why people are turning to Golang, Rust, and Swift. But these languages could follow the same path.
Likewese using C++ on Android requires perseverance given the how the NDK is handled and what it exposes, while on Windows it feels nice because it has parity with .NET tooling, specially on UWP.
The Apple ecosystem slowly gains new frameworks like SwiftUI that simply don’t work with Objective C anymore. The platform is abandoning one language similar to Microsoft and their VB story. It will take a long time, but AppKit & UIKit will end similarly to how Carbon ended on macOS.
> More generally, breaking changes to the standard library are not possible.
Could we amend this RFC to introduce a system like this? Sure. Have we? No.
Can you explain this position? Python just did a 10-year deprecation cycle on Python 2, but it was really costly for a lot of people. If they'd just not done that it would have been better; the benefits of removing stuff are relatively minor (cleaner language implementation codebase) and the costs are huge (all users need to change, test, handle compatibility, ...).
Py3 should have simply started to warn when you used "bla" without u"..." or b"...", and so on. To clear up the bad code. Then later ... maybe 10 years later, make "..." equivalent to u"...". Maybe.
IMO the real problem is the near complete lack of static checking that you fixed it with. Sure, modern Python + mypy can do a credible job finding wrong-string-type bugs, but legacy Python 2 codebases don’t have the right annotations.
I do wish I could tell Python 3 to error out if I accidentally put a str and a bytes in the same dictionary as keys.
To gracefully deprecate this behavior, you could start by generating a warning each time an implicit coercion is done. Next, make implicit coercion raise an exception, but provide a way to suppress it. Finally, remove the ability to suppress the exception.
As the GP suggests, you'd do well to similarly deprecate unprefixed strings.
This would all be pretty confusing to explain if you were renaming the string types at the same time, as Python 3 did. I think that's an indication that you shouldn't rename the types. You could deprecate `str` and just use the names `bytes` and `unicode`, which go nicely with the `b''` and `u''` mnemonics anyway.
Python 3 also changed the type of string used for Python identifiers. You'd need a strategy there as well.
It might be convenient to have some type-checking `dict` variants in the stdlib, but I think it's a separate issue from addressing the coercion issue.
It’s interpreted code, ffs. It’s not like you have pre-compiled binaries you need to stay ABI compatible with (like Microsoft did when they introduced TCHAR and co).
For example javax.activation, javax.activity, javax.rmi, javax.transaction, javax.xml.bind, javax.xml.ws, javax.jws, and javax.xml were deprecated in Java 9 (released September 2017) and removed in Java 11 (released September 2018). JavaFX was deprecated in Java 10 and removed 6 months later in Java 11.
Dealing with Java 11 was really painful to the point where it really feels like Python 3 of Java. The company I work for had less issues with migrating to Python 3 than with migrating to Java 11, but your mileage may vary.
JavaFX is not deprecated. JavaFX was extracted from the JDK as a standalone project: https://openjfx.io/
Not all of them; AFAIK, there's no Maven Central artifact for what used to be in the CORBA packages.
We were trying to convince him to sandbag responses a bit. I never followed up but it sounded like we almost had him convinced. My thinking was, I didn't notice the problem until we hit almost 2 minutes. If their website hadn't been so fast I probably would have identified the problem months earlier. Essentially his team were being punished for being so damned good at their jobs.
I wonder how palatable it would be to slowly ramp up a pause in the compiler for "No, really, you need to stop doing this, and soon" types of misfeatures. Not a huge one, just enough so people really notice.
(This will need to be updated to include the things in this release, but I haven't gotten to it yet.)
if let Bar::Qux(x) = foo { ... )Assuming there's not some wider design issue, I'd probably implement an unwrap_qux() method on the Bar enum
Not to mention how much each language differs in how the syntax and behavior is implemented, making it harder for beginners everywhere to ramp up.
Like the smart-match clusterf* in Perl, pattern matching is just too darn clever for its own good.
Rust’s pattern matching is deliberately fairly limited in what it can do; it’s all about destructuring types, not running arbitrary code like Perl’s smart-match (which even so I would not describe as a disaster). It’s conceptually very clean—arguably simpler than what ECMAScript already has. `let PATTERN = EXPRESSION;`, `if let PATTERN { … }`, `match EXPRESSION { PATTERN => … }`, `fn NAME(PATTERN: TYPE)`, &c., everything that can introduce a new binding is a pattern. Some of the uses of patterns are refutable (match branches, `if let`, `while let`), and some irrefutable (`let`, `for`, function arguments, closure arguments).
ECMAScript already has destructuring in various places, corresponding to irrefutable pattern matching; what the proposal’s `case` expression introduces is essentially just refutable destructuring, plus a smidgeon of expression-orientation (`->` corresponding to `=>`, that `case` is an expression that yields a value, rather than a statement) in a place that sorely needs it. This is a very logical extension of the language.
If TC39 or equivalent were to design a new language to actively replace JavaScript (meaning something that all extant JavaScript code should be able to migrate to easily, preferably automatedly), there is no doubt in my mind that the language would be more expression-oriented than ECMAScript is, that destructuring syntax would be brought in line with what we call pattern matching so that there was one coherent concept underneath (probably called pattern matching), and that `switch` would use it, becoming equivalent to this proposal.
The way people use JavaScript these days, these things are useful.
(I write all this as an expert and heavy user of both Rust and JavaScript; I use JavaScript more, most of the time, but prefer Rust. Rust was the first language I learned with each of algebraic data types, pattern matching and expression orientation.)
Pattern matching is a can of worms in the way people think these should work and the way compilers implement them. It holds unexpected behavior in so many ways and it's not at all about language abuse, but about giving a tool that is prone to misunderstanding and flimsy enough for programmers to shoot themselves and others in the foot. And it's not the same pattern matching in Haskell (which is fine) that it is in ES (where it's not).
Fortunately the TC39 proposal looks somewhat stalled and you can already see in the Babel plugin and proposal discussion questions on why or how this does or does not do behavior X, Y or Z or how syntax should be implemented. This is a bad sign on how pattern matching can be confusing, but then maybe this may be a sign it never gets past stage 1. Here are a few soundbites from the proposal:
when true -> ... // ok, if x === true
when undefined -> ... // not ok, this creates a local var called "undefined"
when Infinity -> ... // ok, if x === Infinity
when -Infinity -> ... // Syntax error!
when /.*/ -> ... // not ok, unsupported, surprise surprise.
when {x} = {x:1} -> ... // great, we check if x exists and set a value on it if it doesn't?!?!
when {status = 200} if (status === 200) -> ... // left as an exercise for the reader
Now I just love this one, as it just subsumes many of my concerns: const y = 2;
case (1) { // matching on a value itself, lovely
when y if (y === 2) -> 'does not match', // y is (re)created locally for the if!
when x if (x === 1) -> 'x is 1' // you're a psycho if you write code like this
}
Now, I must confess I do like matching on types as they can substitute method dispatching elegantly and should be simple to understand and implement, but this is more suited for typed languages, not ES. Same for basic parameter existence as a destructuring dispatch for arrays and objects. But never on value, ranges (`[1..=x]` - yuck, Rust!) or anything more complex.Historically, smart match in Perl was "stolen" from the ideas of Perl 6. Perl 6 (now named Raku https://raku.org using the #rakulang tag on social media) learned from the problems found in Perl, and changed its smart-matching model from symmetric to asymmetric. Basically, `a ~~ b` is syntactic sugar for `b.ACCEPTS(a)`. By transferring the responsibility for what smart matching means to an object of a given class, made it possible to make much more sane default behaviour, as well as allowing authors to define their own smart-matching behaviour for custom classes.
Part of the problem is that Perl doesn't have as strong of a type system. (Is is an int or a string, or is is both? The runtime certainly doesn't know.)
It was also symmetric in 5.10 (changed in 5.10.1), which didn't help.
---
The major mistake was not making it experimental from the start.
(The smartmatch feature is why marking things as experimental is now a thing.)
---
If it is carefully designed, it should prove to be useful just like it has in Raku.
I don't see how it can be designed well enough. The fundamental problem is that Perl operators are monomorphic and variables are polymorphic. This was the same problem for things like keys and each on references, for example.
If you actually read what I wrote, you'll note that everything I said makes it a difficult to impossible in Perl.
I meant adding the feature to Rust, which doesn't have the same difficulties.
https://play.rust-lang.org/?version=beta&mode=debug&edition=...
Can this sort of removal not happen as part of an edition? Is it because it's part of std, rather than core, or something like that? It's a bummer, but the commitment to stability is appreciated!
matches!, subslice patterns, and .unwrap messages look excellent!
However, at least the 2018 edition had the requirement that updating can be done automatically. Ensuring that is hard for library functions.
It's already not required to be defined. Since 1.27.0 there is a default implementation that contains a "description is deprecated" msg.
Nothing against Python specifically. I've just tried and failed over and over again to build large programs in untyped languages. I like my types and think they are invaluable, especially when it comes to refactoring.
Rust helps you deal with certain classes of problems. It forces you to write code in a certain way and think in a certain way. Especially if you are using the standard library, you have less flexibility on how you can write your code if you don't want to write a complete mess. In many ways this is an advantage -- especially if you are very experienced with the ecosystem. Programming languages like Python or Ruby and especially JS (which has a terrible standard library) offer more flexibility. However, it comes at the cost that it's easy to get yourself into trouble.
In the end, these things usually come out in the wash. Programming is programming. Some people are always going to like doing things one way or another. I actually love writing code in JS because I find it a very expressive language (and my code looks nothing like most people would expect JS code to look like). In some ways, Rust is like programming in a straight jacket, but it's a comfortable straight jacket :-). I like the shape of code that Rust encourages me to write. As I get more proficient in the language (not so proficient yet), I find that I think less and less about how to do things and more and more about what I want to do -- like any language, really.
The main downside of Rust is that it is huge and opinionated. It takes a long time to learn how to use it well and it really wants to push you towards certain types of solutions. Learning all of these things is challenging.
TL;DR: Yes, you can definitely be as productive in Rust as in Python. And vice versa. Modulus the fact that you will be running into different problems with each.
Also for assertions, but then again there is a crate that provides assert_matches!, which is more useful for this than matches! itself.
"Added tier 2 support for riscv64gc-unknown-linux-gnu"
which hopefully means Rust will make its way to the RISC-V versions of Fedora/Debian (wasn't there when I last checked). Furthermore, there are a lot of things depending on this that might also arrive now.
I'm planning to dive back into it, but this time I'm going to attempt to do all of my programming in such a way so as to not require 'lifetime' semantics.
Not 100% sure how easy it would be to do so, but the theory is that maybe writing code that needs lifetime semantics is an anti-pattern? Rust has just given us the tooling so we can confirm that the "style" we are already writing our code in adhere's to Rust's guarantees...
Lifetimes are one tool in the toolbox, and using them is absolutely not an anti-pattern. Being able to define and use explicit lifetimes is a really powerful tool that allows you to safely share data in ways that can be tricky and error-prone to do in some other languages.
For example, a deserialiser could be passed a reference to a buffer (&'a [u8]) and return a struct where some fields are &'a str which are just bytes borrowed from that buffer. You could then perhaps trim() them - returning another &'a str which just points to a smaller slice of that same buffer, and pass them off to other parts of your program - potentially even pass them to an async function that you await on, which could run in another thread - and the explicit lifetimes mean your code will fail to compile if the buffer can be dropped before the string is.
Is this an anti-pattern? There are many situations where this would be the most efficient implementation. The compiler can frequently elide lifetimes, it only needs them when it's ambiguous. Sometimes you need to pass two references and return a reference.
Tangentially, the word itself is spelled that way to preserve the vowel sound of "unwrap"; without the second "p", the "a" sound would turn from a short "a" to a long "a". A good heuristic for this sort of thing is that a syllable that ends with a consonant will make a short vowel sound, whereas one that ends with a vowel will have that vowel sound be long.
Yes, obviously, but you're missing OP's point. When the keyword is backticked it doesn't really make sense to adjust its spelling based on the addition of the -ing affix, since in some cases this would also require deletion of letters at the end of the keyword -- which clearly should remain unaltered when backticked. Thus, the only sensible and consistent rule is not to make spelling adjustments when adding -ing to backticked keywords.
Not that any of this matters at all!
Otherwise, read through the blog posts listed in previous editions of https://this-week-in-rust.org/
I ask because I've written some trivial rust code (a little TUI client for a database) and while it was mostly fine, I didn't feel myself absorbing the rules by osmosis (I had to wait for the compiler to yell at me and then wrestle).
1. If you are feeling stuck, please reach out on users.rust-lang.org or discord.gg/rust-lang. We're here to help!
2. If you're feeling really stuck, maybe take a break and come back to Rust in a few months. A number of people have given up on Rust in frustration, came back after a significant amount of time, and then said "why did I think that was so hard before?"
3. Don't worry about a few calls to clone when you're getting started; it's better to have a working program that does some extra copying than it is to not have a program at all.
4. Especially when starting out, you almost always want structs to own their data. Don't use references as struct members unless you're absolutely sure that's what you want. Advice #3 helps with this.
5. I think one of the biggest mindsets that can set you up for failure with Rust is "The compiler says no, how do I get it to do what I want anyway" instead of "The compiler says no, what is it telling me, and how can I work with it instead?" Especially if you come from an OO heavy background, you may need to change the patterns that you reach for initially. Yes, you can write any style in any language, but Rust pushes you towards its idioms much more strongly than other languages do.
Why not plug the rust channel on Matrix?
* Things that are run by the project
* Things that I use
I have no idea if the Matrix channel is any good or not, so I cannot recommend it.
For drills, I recommend exercism.io in practice mode (mentor/student ratio is pretty bad). After working on your own solution for a while, then checking how the others solved it... priceless. Somebody almost always figured out an objectively better solution.
Over 5 years of Rust experience, 2 of which are professional.
Looking back at the very first Rust module I wrote, it's actually not half bad. I thought it was bad at the time because I was continuously fighting the borrow checker, but now I see that the borrow checker led me toward fairly idiomatic Rust patterns.
BTW, if you're coming from Javascript or Python, the closest thing to a JS/Python string in Rust is Rc<String> or Arc<String> (depending on whether your code is multithreaded.) I don't want to admit how long it took me to figure that out and I think that little bit of wisdom ought to be featured prominently in the Rust book. :-)
I do everything this way now (JS, python, etc...)
At the beginning it was a case of write the code, then see how the borrow checker complains and fix it. Now that I understand the semantics intuitively, for the last year or so, it's very rare for the compiler to complain about anything. I guess I've internalised the semantics -- they're not exactly arbitrary, mostly the lifetime rules just follow from what you should be doing anyway.
One thing I would encourage (and this applies in general) is trying to understand _why_ the compiler is complaining about some code you wrote before attempting to "make the error go away". Don't just hammer away until you make it work; that's not how you learn. I've seen a lot of Rust code that's full of unnecessary use of Cell, Rc, etc because people didn't take the time to understand the semantics and just reached for ways to "make the errors go away".
I guess I’d rather have the feature for when it’s truly necessary than exclude it from the language entirely, and just hope that people use it with forethought.
Maybe macros make sense as a pragmatic way to allow prototyping of future language features. But language designers can, and IMO should, work towards making them unnecessary in the production-code ecosystem, because the disadvantages for readability and tooling are very real. And that's only going to get worse as more advanced tooling (IDEs, profilers etc.) becomes available and people rely more heavily on those tools to be deeply integrated with the language, because those tools are never going to be able to understand ad-hoc macros.
What frunk does at runtime (well, its proc macros are still compile-time, but the code that makes use of the metadata is runtime), syn does at compile-time. I can understand that it's nice to have a simpler model where everything happens at runtime, but the things that proc macros can do include things that matter to the typesystem. For example, some of the macros I've written generate new types based on the input tts. You can't do that at runtime.
>And that's only going to get worse as more advanced tooling (IDEs, profilers etc.) becomes available and people rely more heavily on those tools to be deeply integrated with the language, because those tools are never going to be able to understand ad-hoc macros.
The mainstream tools (rls and rust-analyzer) get their information from the compiler, and thus have access to the expansion of the macro rather than just the macro invocation.
Again, though, that's something you want to be able to do in a standard first-class way in the language, rather than needing a custom macro. Depending on the use case you either want higher-kinded types (for transformations like producing a "patch" version for a given record, where all members are (recursively) optional) or dependent types (for transformations where you really just want to do a bunch of specific type->type cases). It's really not rocket science - Scala has been doing this stuff for years already.
> The mainstream tools (rls and rust-analyzer) get their information from the compiler, and thus have access to the expansion of the macro rather than just the macro invocation.
Sure, and that helps, but the tool can't possibly know what the full relation between input and output is (because by definition the macro is an arbitrary function), so it's never going to be as reliable as first-class code.
The macro can associate output tokens with the spans of input tokens, either by reusing the input tokens (`#[derive(Foo)] struct S;` -> `impl Foo for S`, the `S` is reused), or using the macro API which allows specifying an arbitrary span for new tokens. This is required both for ident hygiene and for associating errors with the source code, thus it is a core part of the macro API. And since the compiler has this information, tools have it too.
That means if you have
#[foo]
struct S; // (1)
let s = S; // (2)
and use an IDE to refactor-rename `S` to `S2` at the line marked (2), the IDE has enough information to know that it must also rename the `S` at the line marked (1), even though it's a macro input. This applies equally well to function-style proc macros `foo! { S }`Agreed. But in order to receive that bug report, you need a macro system. This is partially because...
> Rust is crying out for a way to treat structs generically (i.e. what frunk does with LabelledGeneric)
frunk is very cool, and I'm glad it exists, but it gets about 100 downloads a week. The matches macro that we uplifted to the standard library in this release is getting around 20,000.
> the disadvantages for readability and tooling are very real
Interestingly enough, just this week rust-analyzer gained the ability to do code completion within macro invocations.
Indeed, because library authors find it easier to write their own macro to solve the problem. And maybe part of that is that frunk is too hard, but I do think it's also the case that macros are too easy - or rather, that the ease of adding them is not proportionate to the ease of maintaining codebases that use them. http://www.haskellforall.com/2016/04/worst-practices-should-...
One of the first things I wrote was a procedural macro. Yeah, I like to learn things the hard way, but I'm glad I did! Once I got past the confusing split between proc_macro::TokenStream and proc_macro2::TokenStream, I found that using "syn" and "quote!" is really quite enjoyable.
I'd rather have a small simple concise 1-liner, than being forced to write 20 lines of code otherwise.
In Go you have `if err != nil` on every other line it seems and that makes things hard to read. The `try!` macro which later became the `?` makes things much easier to read IMO. Especially when you have to drill into something nested.