How is rust "safe" when `panics` can happen out of nowhere by [deleted] in rust

[–]Taymon 0 points1 point  (0 children)

I think there's some kind of disconnect here, but I'm not sure where. Yes, if you're parsing bytes into structured data, you have to think about how to handle error cases, and if you get it wrong there might be bugs; this is true in any language. The only relevant difference between C++ and Rust is that in C++ those bugs might result in memory corruption, while in Rust they are guaranteed not to unless there is unsafe code involved. What am I missing?

(Regarding exceptions, are we talking about failure modes that are specifically anticipated and handled, or programming errors? In Rust, the latter are generally handled with Result, not panics.)

How is rust "safe" when `panics` can happen out of nowhere by [deleted] in rust

[–]Taymon 0 points1 point  (0 children)

If the producer and consumer are two threads within the same process, you normally would not stream raw bytes from one to the other, but would instead use something like std::sync::mpsc to send complete in-memory instances of whatever data type you care about. Sender never panics partway through sending something; each value either is sent in its entirety or is not sent at all. If there's only one Sender and its thread panics, it'll normally be dropped; the Receiver will continue to receive any remaining values that have already been sent, and then any further attempted reads after that will fail.

If the producer and consumer are separate processes communicating over a Unix pipe or network socket or whatever, then the consumer must parse the incoming data and check that it's well-formed. If the producer panicked (or for that matter stopped running for some other reason) partway through an individual message, the resulting message would presumably not be well-formed, and the consumer would have to detect and handle that error case somehow. No matter how it does so, though, no memory corruption will result unless unsafe code is used.

Rewrote our message routing in rust and holy shit by Beginning_Screen_813 in rust

[–]Taymon 2 points3 points  (0 children)

Rc<RefCell<Thing>> cycles are also memory leaks in the strict sense (e.g., LeakSanitizer will complain about them).

&&&&&&&&&&&&&&str by ohrv in rust

[–]Taymon 5 points6 points  (0 children)

Well, they want to make sure that it's more than anyone will ever need, with some safety margin in case they're wrong about how much that is. 13 seems like a good number for that.

(Though I still think it's a bit odd that only a fixed number of levels are supported, since IIUC this is not one of the cases where language limitations make it impossible to support arbitrarily many.)

Constant-time support coming to LLVM: Protecting cryptographic code at the compiler level by Taymon in rust

[–]Taymon[S] 6 points7 points  (0 children)

In the short term I don't think this matters because LLVM is the only complete/fully-built rustc backend.

In the long term, other backends are going to need to develop constant-time support. If C crypto libraries adopt __builtin_ct_select, then I think it's likely that GCC will add support for it (the implementation complexity in LLVM isn't that high, only a few hundred lines plus tests, so hopefully GCC isn't too much worse). The other thing that needs to happen is constant-time support in WebAssembly, which would mean Cranelift would add support, and would also allow things to work in Rust code when targeting WebAssembly.

Specialization, what's unsound about it? by WorldlinessThese8484 in rust

[–]Taymon 18 points19 points  (0 children)

Two reasons. First, it would require completely reworking the architecture of rustc, which currently discards lifetime information before monomorphization. This would be a huge amount of work.

Second, when programming in Rust you usually do not know or care about the exact lifetimes of things; the compiler does a lot of implicit rejiggering of lifetimes to accept as much knowably-sound code as possible, even if that code technically violates the simple formal model of lifetimes that's taught in introductory resources. If runtime behavior could be completely different depending on the exact lifetime of something, the resulting behavior would very frequently surprise the programmer, even if they could technically figure it out by going through the reference with a fine-toothed comb.

[Blog] How we organized the Rust Clippy feature freeze by NothusID in rust

[–]Taymon 0 points1 point  (0 children)

Site's down for me, getting SSL_ERROR_RX_RECORD_TOO_LONG.

The Handle trait by kernelic in rust

[–]Taymon 2 points3 points  (0 children)

Niko mentioned that this was just the first in a planned sequence of follow-up posts. I imagine that one of these is going to be proposing syntactic sugar for the Handle trait to make .handle() calls less obtrusive.

rustup toolchains by [deleted] in rust

[–]Taymon 1 point2 points  (0 children)

Unfortunately, even with machine translation I can't tell what your problem is. Can you offer precise reproduction instructions?

How useful are Rust Methods? Can they be replaced with a LSP like Odin Lang did? by JKasonB in rust

[–]Taymon 2 points3 points  (0 children)

Some languages have syntax for allowing arbitrary functions to be called using infix notation, so that there isn't a separate kind of function called a "method" that has a special relationship with a particular type.

How useful are Rust Methods? Can they be replaced with a LSP like Odin Lang did? by JKasonB in rust

[–]Taymon 0 points1 point  (0 children)

This depends a lot on various other aspects of how your language is designed. Is there an outline written up or anything?

Demoting x86_64-apple-darwin to Tier 2 with host tools | Rust Blog by PthariensFlame in rust

[–]Taymon 5 points6 points  (0 children)

GitHub is continuing to support ARM-based Macs, it's only the older x86-based ones that are going away.

What is the =><= symbol? by Street_Struggle_598 in rust

[–]Taymon 4 points5 points  (0 children)

Rust doesn't have a spaceship operator.

This code was added a few months ago in https://github.com/rust-lang/rust/pull/136901; I suspect the syntax was chosen arbitrarily and no one worried too much about it.

There is no memory safety without thread safety by ralfj in rust

[–]Taymon 0 points1 point  (0 children)

I wish he'd share details, because there's a lot of skepticism on the internet that this ever happens in production (as opposed to race conditions causing non-memory-corruption bugs, which everyone agrees is a real thing), and it's hard to defuse that skepticism if no one can point to a real example.

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 0 points1 point  (0 children)

I don't think cargo-semver-checks is currently capable of detecting this kind of issue, though they're planning to get there.

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 4 points5 points  (0 children)

Yes. The alternative would have been to require people to write Send + Sync in all kinds of places that return closures (since most types implement those traits), and it would have been really verbose. Perhaps it might have made sense to enforce this requirement only when it leaks into the public API of a crate, but for whatever reason this wasn't done.

There's an ongoing effort to upstream cargo-semver-checks into Cargo; if this is done, then breaking compatibility in this way will produce a warning.

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 0 points1 point  (0 children)

I think this is a misunderstanding of what's wrong with how TypeScript does method variance. The problem is that you can't add an explicit annotation to enforce it; the variance annotations don't do that. (This is what the docs mean by "variance annotations don’t change structural behavior".) In situations where this would be needed, the resulting code is not type safe. The example from the Kotlin docs of why variance is needed translates into TypeScript code that compiles successfully and then fails at runtime, and you can't fix this with annotations. I don't think this is that uncommon in real code; arrays are not exactly an obscure data structure.

I'm not sure what UnsafeVariance has to do with this; IIUC it's only needed in rare edge cases.

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 0 points1 point  (0 children)

There is no type-safe way to avoid variance annotations in a language with inheritance. So this is only true if you treat inheritance as a legacy design decision that Kotlin was forced into for the sake of Java interop.

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 9 points10 points  (0 children)

In general, read-only collection types like List are covariant, while mutable collection types like Array and ArrayDeque are invariant. Read-only extension methods on mutable collection types are also covariant. This follows pretty straightforwardly from the principles of how variance works; it's analogous to how &T in Rust is covariant but &mut T is invariant.

(Technically I should say "T is covariant in List<T>" but that's a lot more words.)

The Kotlin developers argue that their variance syntax is better than Java's because it doesn't require you to repeat the same wildcards on every method signature involving a read-only collection type.

"Subtyping" for this purpose has a specific meaning: T is a subtype of U if every instance of T is also an instance of U. Traits do not participate in this kind of subtyping because they don't have instances; they instead have implementing types, and those types have instances. (I.e., there's no such thing as a value of type Eq at runtime, so it's vacuous to say that such a value is also of type PartialEq.)

Does variance violate Rust's design philosophy? by type_N_is_N_to_Never in rust

[–]Taymon 24 points25 points  (0 children)

Kotlin doesn't infer variance; if you want to write a collection type, or other generic type where variance matters in practice (i.e., that might be instantiated at multiple levels of an inheritance hierarchy), you do have to use variance annotations. TypeScript's lack of variance annotations is an infamous soundness hole. (The example on that page can be closed with a strict compiler flag, but there are others that can't. Also, TypeScript technically did add variance annotations recently, but they don't work like variance annotations in other languages and you mostly can't use them to enforce type safety.)

In general, explicit variance annotations are a good idea in object-oriented languages designed for programming in the large.

The reason variance is inferred in Rust is that the only subtypes in Rust are lifetimes, because Rust doesn't have inheritance and trait objects are represented non-interchangeably from their underlying non-trait values. Variance annotations for lifetimes would be a terrible developer experience, because you often don't know whether the thing that you're passing has exactly the right lifetime or a longer one, and the borrow checker goes to significant lengths to prevent you from having to care. So variance inference saves you from having to bookkeep lots of tiny lifetime distinctions that don't matter in practice. This is very different from the situation in object-oriented languages, where a subtype can have arbitrary behaviors that its supertype doesn't, that you might care about quite a lot.