#Things you'd love to see in Rust 2.0

1779 messages · Page 2 of 2 (latest)

night cedar
#

(also an unsafe { shut_up_and_trust_me() } proof is there when you don't really want to do the whole formal proof thing)

high ivy
#

just add dependent types to rust ferrisClueless

night cedar
#

Duh doi

delicate warren
#

personally i prefer my code only takes one day to type check

delicate warren
#

without genuine inductive types (including strict positivity checking), i feel like any Rust system including type theoretic proofs won't actually be useful

tame eagle
#

recursive generic bounds that don't overflow

slim elbow
#

An std lib that contains more basic features so I don’t have to use crates for basic programs, like a random number generator

plush saffron
#

counter point, the rand crate has changed its api a lot over the past few years alone. If the std lib included random number generation we would likely be in a situation like python's std lib, "Oh yeah that works but it has been deprecated for years, you should use this instead"

covert solar
#

yup, rand has changed quite a lot

#

you can see in the update section

#

every major release makes things a bit different

#

plus with a dedicated crate you can jam in more stuff like new random algorithms

#

i don't think most random libraries in stds have the ability to specify the random generator and distribution for the output

covert solar
#

like JS's Math.random

#

is there anyway for you to configure what it does? no

tribal stratus
#

Although C++ does have the extremely dumb restriction that you can't generate random bytes

covert solar
#

Java's Random/SecureRandom.next*

tribal stratus
#

The result type generated by the generator. The effect is undefined if this is not one of short, int, long, long long, unsigned short, unsigned int, unsigned long, or unsigned long long.

#

Notice the lack of char, signed char, or unsigned char

#

Apparently, the justification is that they're "character types" and not "integer types"

plush saffron
#

lmao

#

this is what happens when you conflate "character" and "integer" types

#

although C integer types are already a mess

lucid raft
#

We don't need Rust 2.0 to add things to the standard library. That can be done right now. However, as mentioned, there are good reasons to keep Rust's standard library fairly minimal. Especially in these early days of Rust.

#

That said, there is definitely an interest in the standard library having something like a getrandom function that's simply a primitive wrapper around the OS function for filling a byte array with randomness. So it could happen one day. See also: https://crates.io/crates/getrandom/0.2.7

high ivy
#

Especially given that std already accidentally exposes a worse version of that through hash_map

tame eagle
#

true

#

for Rust 2.0 I'd like the standard library to be objectively correct on every matter

vale fractal
#

Tbh, I think we could use adding rand and a few friends to the libstd docs, clearly noting them as "this is available by adding the rand crate".

The crates under rust-lang on github are basically libstd (except not forever stable), but I feel people don't quite realize that and think they're "just" third party stuff

covert solar
#

that'd be really cool

#

like a seal of approval for the really common crates

wanton wagon
vale fractal
#

Huh? I thought it was

wanton wagon
vale fractal
#

Nevermind that, points stands for other crates :D

wanton wagon
#

I would love for more crates to follow that process though

vale fractal
wanton wagon
#

and be under the jurisdiction of T-libs

wanton wagon
#

They should introduce the RFC process to crates if they want an “std++”

#

Which I think would be a very good idea actually

vale fractal
#

Ok, fine, but a seal of "this is reliable" would be nice

wanton wagon
#

yes, that too

#

But I think the seal couldn’t be justified with the current development model of those crates

lucid raft
#

How would a seal of approval work? Would it just be a passive "the libs team trusts these authors to maintain this crate"? Or would it be something the libs team is actively involved in overseeing?

wanton wagon
#

I’d prefer the latter

#

I think it should really be an “extended std

#

and so should copy everything except #![feature]s and being built-in

forest quarry
#

ehhh the rfc process is more needed for the stdlib than external crates

#

i'm not entirely convinced it's needed when things aren't perma stable

night cedar
#

id be tempted to try doubling down on the small std and outright remove std

#

only have core be lang-provided

#

and make it totally clear that external deps arent a bad thing

#

keep increasing the usability of dependencies

vale fractal
#

You'll need to solve the security problem tho

#

People already complain enough now about how the average Rust crate has way too many deps to be easily auditable

forest quarry
night cedar
#

agreed :) I think crev hints at some genuinely good ideas and isnt able to carry them far enough because of its small userbase

forest quarry
#

so you'd need to make stable all of them

night cedar
forest quarry
#

and what would the MSRV of std be

night cedar
#

2.0 lol

forest quarry
#

i hope to god it's not something low

#

like
std can currently just invent a feature and then use it

covert solar
#

pretty much

vale fractal
#

We also have several std lang items that aren't in core (such as "what does panicking do")

covert solar
#

i mean like isnt std using yeet_syntax atm

forest quarry
#

std has like 100+ feature flags

#

lmao

lucid raft
#

std is actively trying to reduce the number of unstable things it uses.

night cedar
#

^

forest quarry
night cedar
#

in general, im assuming that questions like yeet and panicking are solved. because that feels like the point of a 'what does ideal 2.0 look like' thread

#

(also there're a decent number of features which you could model differently to features)

lucid raft
night cedar
#

Some are solvable with temporary rustc-wacky-feature-x crates which cargo will only be able to resolve on compatible versions

lucid raft
night cedar
#

if we're shifting to totally requiring deps for anything meaningful, how can we make that the best experience we can?

#

to me, an improved crates story would be huge for a rust 2.0 (though we can probably do a lot inside 1.0 too :P)

#

For security, I really like the idea of the web of trust. I think it formalizes the ideas behind why we have more trust for std today: there's an idea that the code in the rust repo is being looked at by a number of trusted maintainers from the libs team

forest quarry
#

we already do require deps for anything meaningful tbh

night cedar
#

if that's a first class concept in cargo, then the default install of rust can come with default trust for some rust team members

#

and so the user experience doesn't change much, but now also helps with trust levels of external crates that are popular enough for libs teams members to be interesting in giving them feedback

#

from my pov, that alone would be a big big shift

night cedar
#

the way FOSS development works today, we're not going to get away from these dep trees

forest quarry
#

and dep trees are good imo
the alternative is vendoring

#

and vendoring turns 1 bug into N bugs

night cedar
#

to an extent, yes

#

and then there're the real problems around increased code complexity

#

when you look at dep trees of a lot of popular apps and consider whats actually needed, the depth of the tree is normall like double what it should be, and there're 3x the libraries, and if it were all bespoke there'd be 1/4 the code

#

sometimes those numbers are a lot worse, too

#

developing ways to systematically control that is a worthwhile goal

#

tho im not sure what the solution will be

vale fractal
#

How do you define "what it should be"?

#

Or in general, what sources are you taking those numbers from?

night cedar
#

the numbers are coming from a few projects which ive needed to slim down, which have given me some gut impressions of how much got removed

#

doing an actual survey of public projects would be fantastic, but a lot of work :P so im basing it off my intuitions in the meantime

#

but it's definitely not rare to cargo tree some of the projects ive helped with and be able to hack off >100 dependencies while only adding like 100 lines of helper functions to the main project

vale fractal
#

Ok, that does sound plausible

#

Though that is a lot of deps

#

How many were there overall?

night cedar
#

thats for all sorts of reasons - features are sometimes enabled when they dont need to be, duplicated dependencies add up fast, and once in a while you really can just rewrite the small bit of a library that you need

night cedar
#

it was a nasty case, but not particularly rare for current Rust application projects

#

i think veloren is pushing that

vale fractal
#

Acrimon recently told me that Veloren is past 900

#

Not pushing 450

night cedar
#

whew, yeah thats impressive

#

thats probably across the whole project

#

between the client and server apps

vale fractal
#

That sounds likely

night cedar
#

and yeah, duplicated dependencies are more common than they should be

#

we're having the compiler compile the same chunks of code up to three times

vale fractal
#

Yay for semver incompatibilities

night cedar
#

definitely need a process for propagating updates out into the ecosystem more painlessly

#

& I really like burntsushi's thing of retargeting previous versions of his crates to the new version

#

was called something like the semver trick?

#

but its brilliant

forest quarry
vale fractal
#

Wait, isn't the point of semver that if this is possible it happens automatically, and you can state that a breaking chante happened?

#

The semver trick refers to publishing a breaking change to a Rust library without requiring a coordinated upgrade across its downstream dependency graph.
Isn't this just making a breaking change without saying so in semver with extra steps

night cedar
#

nope :)

forest quarry
#

it makes a breaking change to some part of the library

#

without affecting others

#

like

#

normally, foo-lib0.1::SomeType and foo-lib0.2::SomeType are different types

#

even if SomeType didn't change at all

#

basically you want per-item versioning

vale fractal
#

So, it's still a breaking change, and you didn't say so. So I may be broken by your stuff

forest quarry
#

SomeType didn't break

night cedar
#

so rs mod cratev1_12 { pub fn foo() { /* complex logic */ } } // I want to publish a different api mod cratev2_0 { pub fn foo(v: u8) { /* complex logic */ } } // but the two implementations are *fairly* similar, and now consumers are having to compile both crates! // lets make one final update to v1 mod cratev1_13 { pub fn foo() { super::cratev2_0::foo(3) } }

forest quarry
#

and you're still marking breaking changes as breaking changes

vale fractal
#

Are you now? Wasn't the idea that you can use the changed item as its old version?

night cedar
#

(this is also relevant to types like 522 said, but im focusing on the dependency graph perks atm)

forest quarry
#

pretend SomeType is some very commonly used item that didn't change

#

that's not why you made the breaking change

vale fractal
#

So I can update from 0.2.0 to 0.2.1, and this breaks me, because I'm now secretly pulling in a type from 0.3, which doesn't match the properties I need

night cedar
#

(nope)

forest quarry
#

you made the breaking change to remove some deprecated functions or whatever

vale fractal
#

Where are you taking that from

#

It's not in the link

night cedar
#

btw can i paused this here

#

and say

forest quarry
#

did you read the example in the readme
i made up SomeType

night cedar
#

read the whole thing on the github

#

its a good explanation

vale fractal
#

I did

night cedar
forest quarry
#

okay so let's use their names

#

i have c_void which didn't change

vale fractal
#

What I see is that a int32_t has become a uint32_t, and it has done so in the old versions too

forest quarry
#

c_void on 0.2 and 0.3 is identical

#

and i should be able to use a 0.3 c_void passing to a 0.2 crate

vale fractal
#

Ok, that is fair.

forest quarry
#

that's the trick

#

making c_void the same type

#

the thing that actually changed is a breaking change, yes

vale fractal
#

So, you basically export unchanged later items back in time?

#

Is that the thing

forest quarry
#

yessss?

#

yes

vale fractal
#

Thanks. That does make sense

forest quarry
#

old crates get patch updates that make them depend on newer versions

night cedar
#

cool beans, youve got it :)

#

& the same trick is also great for dependency tree cohesion

forest quarry
#

it's kinda cursed

#

and it would be nice if this was like

night cedar
#

every past version with a published "backport"

forest quarry
#

more formalised

night cedar
#

has only one dependency

vale fractal
#

As opposed to the link, with, I'm sure burntsushi meant well, but this information, reading it again, is scattered across several paragraphs of "this would be a painful breaking change"

night cedar
#

totally agree

vale fractal
#

I wonder if we can automatically detect that two types are identical across crate versions

night cedar
#

if we could standardise doing this for all version upgrades, and ensure that its done correctly with semver checks

#

thatd b v nice

night cedar
#

its easy at the type level

#

but sometimes crates have untyped semantics which changing would be considered a breaking change

forest quarry
#

though that seems easy to break type invariants by mixing functions from different crates

night cedar
#

which is why rust has avoided this feature so far

vale fractal
night cedar
#

soo

vale fractal
#

I can't think of an example

forest quarry
#
// my crate 0.1
struct Foo(u32); // SAFETY: is always even

// my crate 0.2
struct Foo(u32); // SAFETY: is always odd
#

how do you not merge these?

night cedar
#
/// you will always get double a
fn foo(a: u8) -> u8 { a * 2 }

/// new version of crate:
fn foo(a: u8) -> u8 { a }
#

yeah same idea

vale fractal
#

Oh, that

#

The function one might be not a problem, tho?

#

The type one is definitely an issue

night cedar
#

the function one is definitely also a problem

#

any documented behaviour of a function needs to stay

#

without a major version change

#

if i have a log("foo") function which tells me that it will write foo to stdout, id definitely be upset if it started sending it to a text-to-speech engine in a new minor version

vale fractal
#

True. My point is just that if this was a method, you could still consider Self compatible with its old self, (tho you may want to say that behaviour depends on the caller's version)

night cedar
#

well u cant really. at least not with current rust

#

and it would lead to weird questions around soundness if you tried to add it

vale fractal
#

I'm extrapolating from my idea to allow some libstd change on editions, such as changing Vec::push's return type.

#

That should be trivial

forest quarry
#

to return a &mut T?

vale fractal
#

Yup.

night cedar
#

there's state invariants inside the vector that need to be maintained, but you can change each function

#

bc they're basically being "namespaced" by edition at that point

vale fractal
#

Yup.

night cedar
#

youd have a v2021::Vec::push and v2024::Vec::push

vale fractal
#

With the two Vecs being the same type.

night cedar
#

yup

#

but i do come to the conclusion that we cant make the backporting thing automatic

#

because APIs can change, the backporting just works because you can normally define the old API in terms of the new one

#

as long as you're generally adding features over time

vale fractal
#

I feel this could be solved with attributes. Maybe you could cfg on your crate version?

#

But of course, that would be ugly to read

night cedar
#

sorry, can u explain what ur solving?

vale fractal
#

I'm formalizing the semver trick a bit more

#[cfg(crate_version = ..)]
pub enum c_void = ...

#[cfg(crate_version = ..=2.0)]
pub const EVILFT_AIO: u32 = 2;

#[cfg(crate_version = 3.0..)]
pub const EVILFT_AIO: i32 = 2;
#

This may want special compiler/rustdoc handling, so perhaps it could be a proper attribute rather than fall under cfg's "umbrella"

forest quarry
#

or only when you need to do the trick

#

that seems verbose af

#

i think a #[same_as(crate::c_void, 0.2)] or something would be better

#

as an attribute on the 0.3 c_void

#

hmmmm

vale fractal
#

That is probably a better idea, yes

#

The "attributes on all the things" idea was inspired by std, but it is ugly

summer linden
#

Maybe this?

#

This would allow a set of "blessed" extras which come with the toolchain but can still version independently of stdlib

forest quarry
#

serde_json DoS (not unreasonable)?
update your compiler

#

std doesn't really deal with untrusted input that much

summer linden
#

...as opposed to the rust 1 status quo where you have to run cargo update to get updates for crates?

#

The crates would still be available to update from crates.io like usual

forest quarry
#

sure but where did you get your compiler

#

package manager or rustup?

#

also

#

security updates for std are pretty rare

#

i'm not sure how big this library is proposed to be but

summer linden
#

Presumably they'd be more common for these contrib crates

forest quarry
#

yes

#

and you need to ship the whole rust toolchain

#

to update any one of them

summer linden
#

Not necessarily

#

Having them along the standard rust install does not preclude having them on crates.io

forest quarry
#

so
would we ship security updates to them?

#

or would it just be "eh they get updated when they get updated, every 6 weeks"?

#

if we do: that's a lot of noise and repeated updates

#

if we don't: why are we even shipping them

#

if they're unsafe to use (sometimes)

summer linden
#

They'd normally be shipped on the 6-week release cycle, but security updates can occur in the middle of a release cycle

forest quarry
#

okay so
like
what's the benefit

summer linden
#

It's supposed to provide some of the benefits of a large stdlib

#

While not having its drawbacks

#

It'd also be a start to get people to not treat ecosystemic dependencies as universally toxic, by providing a sampler of crates that are outside std but well trusted and already on your system to use

forest quarry
#

you're already inheriting one drawback of a large stdlib, updating it is tied to the compiler version

#

making using an old compiler (see: debian) more dangerous

high ivy
#

using debian stable is already dangerous enough ferrisClueless

summer linden
#

I think we can agree to disagree based on the tradeoffs involved 🤷

forest quarry
#

okay like
does it have to literally be shipped

#

or can it be a version number

#

and then it's downloaded from crates.io as needed

light ridge
summer linden
#

There exist other distributions that maintain and provide truly archaic versions of things

#

Such as red hat enterprise linux

#

So all that would be different is that there would be a debian-shaped hole in distro history, with other distros taking up its space instead

unique thunder
#

to_lowercase/uppercase should return a Cow<str> rather than always allocating a new String

#

a relatively minor change, but breaking nonetheless

high ivy
wanton wagon
forest quarry
wanton wagon
#

yeah, but that’s more dynamically typed

forest quarry
#

what does .lowercase do

#

what does the iterator look like

wanton wagon
#

actually, I suppose it’d be mostly the same

forest quarry
#

yes but 'this doesn't exist

#

is kinda my point

wanton wagon
wanton wagon
#

LendingIterators would be stable

wanton wagon
forest quarry
wanton wagon
#

I’d probably be able to think up a better API if I studied the Unicode sources

wanton wagon
#

lending iterators are great

forest quarry
#

"sorry fam you can't move this iterator"

wanton wagon
#

Not while an item exists anyway

high ivy
#

this API would be pretty annoying to use though

wanton wagon
#

How so?

high ivy
#

Well, if the old API was still provided it would be fine

#

But it's definitely more annoying to use than the old api

#

Having to manually work with the iterator

vale fractal
#

Most of the existing Iterator API would just work with lending iterators

#

Though implementing the methods would take more effort.

#

You lose collect, and even that one can come back with some bound for "the associated type doesn't actually borrow self"

#

You'd technically also lose chunks, which doesn't exist anyway on arbitrary iterators

#

In general, all methods that pull at most one element at a time, which is basically all of them, would work fine

forest quarry
vale fractal
#

We're assuming Rust 2.0, aren't we?

In Rust 1.x, they could solve it using whatever mechanism we add to solve the collect problem, if any

#

It just needs a bound on "the associated type doesn't use its generic lifetime", which I believe is possible but don't remember the syntax for

#

If we made this bound implicit if you don't even name the GAT lifetime on the associated type (so, like all existing Iterator impls already do), that'd probably work out.

#

(not saying it's necessarily a good idea to make this implicit, but we could)

wanton wagon
#

I love how this thread is all the regulars constantly forgetting that it’s supposed to be rust 2.0

#

evidently we have developed an instinct to always consider backward compatibility first thing

high ivy
#

wouldn't rust 2.0 break things ferrisThink ferrisClueless

tame eagle
#

the prompt just says things you'd like to see in Rust 2.0

#

which would obviously include things that could be included in the indefinite future of Rust 1.x, but aren't

night cedar
#

Here is an example of running a program inside another program. normally a programming toolchain only lets a gigantic IDE debugger tool do this, but this is a built in feature of the Beads language, that one program can run another. You can freeze the execution of a program, and then rewind its state to some previous point.

While frozen, you ...

▶ Play video
#

this ^

#

because its cool

#

have no idea how it would properly fit into rust lol

vale fractal
#

Time-travelling debuggers?

#

There's one that supports anything LLVM, but only on linux.

#

There's even a riir of that tool, though I don't know how good it is

pearl grove
#

I'm assuming you mean rr and rd (the rust rewrite), and they are quite neat

night cedar
#

more the high level of reflection into the gui :D it'd be hard to manipulate debugger objects from Rust's runtime code

tame eagle
#

maybe more intuitive syntax for closure variable derefs..?

#

|&x| x why does this deref x lol

crude prairie
fleet jungle
tame eagle
#

that's actually what prompted me to come here!

tribal stratus
#

Really, it's just how pattern syntax works; &x takes a reference and then copies x by value

fleet jungle
fleet jungle
tribal stratus
#

Just the clarity?

fleet jungle
#

seeing a clone in code automatically triggers a red flag in my mind unless its preceded by Arc:: or Rc::. I think that copying looking more explicit in code than just another clone is a good thing.

crude prairie
#

same principle as why iterators have cloned() and copied()

plush plaza
#

support for different casing without using the case decorator

tame eagle
#

?

vale fractal
# tame eagle ?

I'm reading this as a complaint about needing to write #[allow(non_snake_case)] to get rustc to not complain about that.

vale fractal
#

It's a pattern, patterns follow the rule of doing the opposite of the same syntax in an expression

#

If we removed some magical syntax, we could have let ref_a = Ref(a); and |Ref(x)| x

#

(I don't recommend doing this. Just pointing out it makes the consistency clearer)

lucid raft
#

#![allow(nonstandard_style)] will allow any crime you like.

forest quarry
#

forbid unsafe is good because it is a hard rule against writing it

#

if a project has forbid(unsafe_code), you can be reasonably sure they don't have unsafe

#

without needing to grep for it

#

also, some things that are unsafe don't have unsafe in the name

#

?play

#![forbid(unsafe_code)]
#![allow(dead_code)]

#[link_section = ".example_section"] fn uwu() {} //~ ERROR: declaration of a function with `link_section`
#[link_section = ".example_section"] static UWU: u32 = 5; //~ ERROR: declaration of a static with `link_section`

fn main() {}
left briarBOT
#
error: declaration of a function with `link_section`
 --> src/main.rs:4:1
  |
4 | #[link_section = ".example_section"] fn uwu() {} //~ ERROR: declaration of a function with `link_section`
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
note: the lint level is defined here
 --> src/main.rs:1:11
  |
1 | #![forbid(unsafe_code)]
  |           ^^^^^^^^^^^
  = note: the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them

error: declaration of a static with `link_section`
 --> src/main.rs:5:1
  |
5 | #[link_section = ".example_section"] static UWU: u32 = 5; //~ ERROR: declaration of a static with `link_section`
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them
lucid raft
#

That's mostly attributes? Because there's no #[unsafe(...)]

forest quarry
#

the workaround is to basically make them trigger an unsafe_code lint

#

so

#

if you forbid unsafe code, at least you won't be able to use them

fleet jungle
barren jacinth
#

have a footgun mode which is unsafer then the unsafe block

#

footgun {}

high ivy
barren jacinth
#

or a flag/macro that fails to compile the code if it has an unsafe block

prisma nebula
#

?eval mode=release

struct Footgun;

impl Drop for Footgun {
    fn drop(&mut self) {
        unsafe{ std::hint::unreachable_unchecked() }
    }
}

let footgun = Footgun;
left briarBOT
#
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11:     9 Illegal instruction     timeout --signal=KILL ${timeout} "$@"
tame eagle
#

forbid(unsafe)

barren jacinth
#

oh nvm

tame eagle
barren jacinth
#

asm!("int 0h10") ferrisForgor

forest quarry
barren jacinth
wicked isle
#

Why does this need to be in a 2.0? This could be added to any version of rust.

tame eagle
#

it doesn't

#

it would be nice to have some API changes to the formatting system too, though

#

Like, I often wish that doing {val:x} would still work if val didn't implement LowerHex—it'd just recursively apply to any of val's fields

#

Basically passing a configuration struct to Display instead of manually implementing 4 different traits

inland cedar
forest plaza
#
  • removal of as
  1. Why?
  2. Wouldn't that be a breaking change?
#

I'd love to see variadic generic parameters if not variadic parameters in general.

prisma nebula
#

👆

blissful depot
#

@agile zealot see ^ and following discussion

#

oh wait wrong ping, whoops

#

@forest plaza

forest plaza
#

And yes, I realized this can include breaking changes.

blissful depot
#

"the message i replied to and the following ones"

#

it's from.. some time ago

tame eagle
#

lol

#

Read up

forest plaza
#

Hmmm...
What if as did From/Into conversions as a replacement for its current behavior?

blissful depot
#

i am not convinced we need it as a keyword at all.

#

we can just call .into() when needed

#

it's extra syntax sugar with minimal added value

tame eagle
blissful depot
#

i'm only against as for value conversions

tame eagle
#

ah, so just removing as conversions

#

yea

#

I agree with that tbh

#

Also a way to convert integers to repr-enums. a Derive impl or some other macro would suffice really; it just sucks that this is thing that requires a dependency or macro_rules for

forest plaza
#

There.

tame eagle
#

: is used for bounds and type ascription

forest plaza
#

Now no as.

forest plaza
#

:flextape:

blissful depot
#

this doesn't solve the problem i'm having. It's not that i have a keyword per se.
It's just its usage as a very funky .into() call

#

as i just said above, i don't think we need special syntax sugar for .into()

forest plaza
#

Right.

#

But now we could fully cobble the word away.

#

Just like we do in real languages.

#

Plus, it's a character shorter now.

blissful depot
#

¯_(ツ)_/¯

forest plaza
#

I mean, disregarding what you said, how is it as a standalone suggestion?

blissful depot
#

Disregarding what i said, i am firmly in a "whatever" on it.
I don't see the value added, but i also don't see any particular problems with it.

But i would guess people who are better versed in type theory might have slight issues with it, since the semantics of that <X : Type>::function() cast is slightly different from where X: Type bound

prisma nebula
#

What cast? The as in <Foo as Bar>::baz is arbitrary syntax, it could in theory be any syntax (preferably unambiguous).

The key word I'd use to describe <Foo as Bar>::baz is disambiguation,
it disambiguates what trait and type combination you're getting that associated item from.

vale fractal
#

Yeah, that's not a cast

#

I just want something nicer for unsizing and float<->int casts tbh

blissful depot
prisma nebula
#

Yes, they're different things

vale fractal
#

Eh, what is a disambiguation if not a restriction of the searched namespace

tame eagle
#

and a pile of secrets

summer linden
forest plaza
#

AND a pile of secrets.

last ocean
#

basic inheritance because it's actually useful sometimes

forest quarry
#

ehhh

#

when?

#

like im not saying it's not
but is it worth the complexity

#

and can it be gotten by different means

light ridge
#

🤮

tame eagle
#

i have never needed inheritance using rust's traits

#

👼

lilac badger
#

range types that are copy

wooden delta
#

Yeah I think it's unfortunate that we can't have a type be both Copy and Iterator without it being a footgun, but I'd much prefer ranges be Copy and IntoIterator than Iterator and Clone.
Though, hmmmm, that gets me thinking, we could just have range Iterators as a thin (non-copy) wrapper on ranges, something like:

// Not an Iterator, but is IntoIterator
#[derive(Copy)]
pub struct Range<Idx> {...}
// Plus all the other basic range types

// Not Copy but is an Iterator
// Maybe with some bound to limit it to RangeBounds types?
pub struct RangeIter<R>(pub R);

(I'm sure this has been discussed to no end elsewhere but if it has, I haven't followed the discussion)

tame eagle
#
  1. decl macros 2.0 would be awesome in general
  2. proc macros without creating a whole separate crate would be nice, too
proud slate
# light ridge 🤮

please be more constructive in your criticism, this just comes across as judgmental and doesn't really contribute to the discussion

forest plaza
last ocean
#

wdym?

#

actually an easier way to just forward a lot of methods would be better probably

#

because that's kind of just budget inheritance

forest plaza
#

That felt clear to me.

last ocean
#

im not familiar with that term

forest plaza
#

I was basically joking in short.

last ocean
#

oh ok

vale fractal
forest plaza
#

@last ocean @vale fractal
What is method forwarding?

#

(Also, I may kinda sound like an asshole, but yeah, I know, the double ping wasn't necessary, I just kinda felt like it in this case.)

forest plaza
forest plaza
#

I mean, that just sounds like inheritance without getting the data-portion.

#

Idkay but that doesn't really sound like a great deal.

vale fractal
#

It sounds a lot better when you account for how common newtypes are

forest plaza
#

"newtypes"?
-- Even if it's "new types" I still don't really understand what you mean.

vale fractal
#

Also, it's not really inheritance: you can't automatically use the forwarder where the "inner type" is expected

vale fractal
#

Oftentimes used to get around the orphan rule

high ivy
vale fractal
forest plaza
high ivy
#

you can write it as a normal struct as well

#

a newtype is just a type that wraps another type and gives it some new meaning, but is often fairly transparent

forest plaza
vale fractal
forest plaza
#

Makes sense. A lot like Go's type aliasing that makes a new type instead of sharing a type.

vale fractal
#

The point is that it's basically the same as the underlying type, but not quite. Sometimes it's to avoid mixing them up, so you do want to not forward methods. Sometimes it's for trait reasons, in which case forwarding would be neat.

forest plaza
#

That'd be cool.

vale fractal
#

You can make a newtype around the foreign one, and now it's a foreign trait for a local type. Legal.

vale fractal
# forest plaza Wish is something I wish Rust had.

As far as I can tell, that's equivalent to automatically forwarding all methods in a rust-style newtype.

Honestly I don't care exactly how it happens. I just want types that have the same API but are different for the purposes of trait impls.

forest plaza
vale fractal
forest plaza
forest plaza
vale fractal
vale fractal
vale fractal
forest plaza
high ivy
#

if the name comes from haskell, it's guaranteed to be weird and not good

vale fractal
#

Call it "coherence" in Rust, it makes more sense

forest plaza
#

Oh boy.

vale fractal
prisma nebula
forest plaza
#

Look at the "who is typing" section.

#

That flex justification, tho.

high ivy
#

without the rules, i could implement Clone for regex::Regex and regex also implements Clone for regex::Regex already and now the compiler would be very confused what it should do if i ask it to clone a regex::Regex

forest plaza
#

We can have forwarded types for some things, then put those in our compositions.

vale fractal
prisma nebula
#

Coherence is analogous to solving the diamond inheritance problem but for impls.

vale fractal
vale fractal
#

Not judging whether it should have, but it's a possibility

prisma nebula
vale fractal
#

Yup.

#

The definition is perfectly reasonable

#

I just realized I was making a cyclic explanation, and phrased my acknowledgment of that incorrectly

lapis plaza
#

1 - only write pointers

let mut arr:[u8; 5];
for i in arr.len() {
    arr[i] = 6; //&write pointer
}

2 - generics being type const

struct example<T:type, E:usize> {
    content: [T; E],
} //no const keywords in generics

3 - const args being generics

fn printnum <N:u8> {
    println!("{}", N);
}
high ivy
#

The second one is basically what C++ does, but I think type by default is nice because it's the most commonly used kind of generic

lapis plaza
#

but its more readable

#

and type type is good

high ivy
#

Also write only references (that must be written to) would be useful in some cases

#

Also called &out sometimes

lapis plaza
#

ye, you can use them in undefined values

lapis plaza
lapis plaza
#

if let some(u8) = T {
}

high ivy
lapis plaza
#

no i think i dont understand what you say

high ivy
#

You might be interested in how the Zig programming language does generics

lapis plaza
#

how

high ivy
#

you pass types as compile time function parameters
I'm not very familiar with it, you have to look it up

lapis plaza
#
  • it does the compiler easiest to understand
night cedar
#

it makes a lot of tradeoffs for that simplicity

#

parametric polymorphism is fantastic and i wouldnt want to give it up in rust

wild glacier
#

a module like Python’s functools would be cool if it doesn’t exist yet

wooden delta
vale fractal
#

Comptime evaluation in Zig is all the worst part of Rust's trait solving, type checking, macro expansion, and const evaluation, all at the same time, in a fixpoint loop

#

Don't get me wrong, it's a really cool feature. However, it's by no means a simple one.

#

Also, you give up on some extremely nice properties. Rust has a rule that if a generic function compiles, then it compiles for all possible type parameteres, no need to check it again for all of them (which wastes times and risks producing errors that you didn't notice, only your dependencies did, so now you have to breaking change to fix it)

wooden delta
# wild glacier a module like Python’s `functools` would be cool if it doesn’t exist yet

I had a brief look at functools, and I think much of it already exists in rust? A lot of it I'm unsure about though.

  • cache seems useful, though I'd expect a rust equivalent might have to be a bit more explicit and might lose some simplicity as a result.
  • Partial is currying, I agree that's useful, though there's probably a crate for it and other function combinators.
  • Cmp_to_key seems like it might be useful, but if it does what I think it does, then it's probably trivially implementable with a lambda or a newtype that implements cmp I'll admit I'm not sure I understand it
  • Total_ordering looks like it does something that rust gives you for free (i.e. implement one comparison function and get the operators for free)
  • Iterators all provide reduce already
  • Singledispatch looks like it's just doing generics, which rust does by default
  • I have no idea what updatewrapper is doing
wild glacier
tribal stratus
vale fractal
#

They're not exactly type errors, tho.

#

They're usually "this thing is too big for the hardware what the heck are you doing" or "Why do you have a type nested more than 256 times"

tribal stratus
#

Well, you can effectively specialize on const generics by using associated consts

fn assert_non_zero<const N: usize>() {
    struct NonZero<const N: usize>;
    impl<const N: usize> NonZero<N> {
        const ASSERT: () = assert!(N != 0, "N must not be 0");
    }
    let _ = NonZero::<N>::ASSERT;
}
#

Luckily, I don't think this is possible for regular type parameters without specialization

thorny pine
#

I haven't read this thread yet, but I'd love to see dependent types, obviously, since I'm the dependent types shill :P

#

Basically fuse an ITP and Rust together.

#

The ease of use of rust + the way higher expressivity and ability to formally verify code where necessary.

night cedar
#

yeah im pretty positive dependent typing is going to be the norm

#

idk how long itll take

#

theres a lot of ergonomics to figure out

tame eagle
#

Kinda wish sort_unstable was 2.0 sort and that sort was 2.0 sort_stable

#

today, I naturally went to sort's docs and it basically says "you probably want to use sort_unstable" so I feel like that should be the one with the shorter name :V

high ivy
tame eagle
#

does that emoji actually mean "but..." like its name implies, or are you just acknowledging

high ivy
#

I have had the same thought before

#

But I think people had good arguments for making stable the default
But I can't remember exactly

vale fractal
#

It's a case of "if you only look at the docs for the minimum possible amount of time, or don't look at all, then you'll land on a sort that is correct for what you're doing"

summer linden
#

C's rand is optimal for "Just quickly give me a number nondeterministically. Kind of. Maybe." but not for actual randomness

high ivy
#

Math.random()

#

Btw, did you know that you should not use Math.random in JS as a crypto rng fallback?
Use it as your primary crypto rng instead to get more predictable results

tame eagle
#

idk how it can cause unexpected behavior to move around equal structs

summer linden
#

Choosing a sensible default choice for rand is hard, because there is no one real good solution for all cases

high ivy
#

And sometimes you need a stable sort but aren't aware that you need a stable sort

tame eagle
#

so basically they opted for the safer default

forest quarry
#

a stable sort is always correct where an unstable one is

#

the reverse is not true

#

but the docs on both should try and teach you the difference

vale fractal
#

I mean, they do, don't they?

forest quarry
#

for example, stable sorting ints (or most primitives) is useless

#

and clippy will tell you that

#

(to use unstable sort)

glad otter
#

Fun fact: there will never be rust 2.0

wooden delta
#

And there will never be a web 3.0

#

But that's not the point of this thread
Oh no I derailed the thread

night cedar
#

web ∞ plz

glad otter
#

Yea I hate web3 so bad

light ridge
#

I hate how web3 has tainted web3

#

Now what do we call an actual web3

wooden delta
#

More specifically, what if we didn't have a single default for random number generation, given that there are at least two reasonable criteria for what a "good default" would do (high speed or high security).

light ridge
#

High security is the only good default

tribal stratus
#

I wish we had F: Fn(...) -> impl Trait bounds, that desugared into F: Fn<(...)>, <F as Fn<(...)>>::Output: Trait

#

tbf that might not need 2.0

#

It would make async closures infinity times easier though

#

F: for<'a> Fn(&'a [u8]) -> impl 'a + Future<Output = ()>

pale whale
#

i wish you could dors fn foo(x: impl ToString) { ... } and you could do foo::<...>

#

or rather, i'd love to not have to explicitly name type parameters if i didnt need to

#
impl Debug for Wrapper<impl Debug> {
  ...
}
``` for example
prisma nebula
# tribal stratus I wish we had `F: Fn(...) -> impl Trait` bounds, that desugared into `F: Fn<(......

Well, you can write equivalent bounds with the trait alias pattern and associated_type_bounds unstable feature:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=708d986e12d767748e234b875f249020

#![feature(associated_type_bounds)]

use core::future::Future;

trait Fn1<T>: Fn(T) -> Self::Ret {
    type Ret;
}

impl<T, P0, R> Fn1<P0> for T
where
    T: Fn(P0) -> R
{
    type Ret = R;
}

fn foo<F>(_f: F) 
where
    F: for<'a> Fn1<&'a [u8], Ret: 'a + Future<Output = ()>>
{}
#

too bad that associated type bounds don't have a direct translation to the closure bound syntax

hybrid salmon
#

I think the problem with this is deeper than missing syntax

prisma nebula
#

Well yes, but that's due to limited support for higher ranked lifetimes, not lack of support for impl Trait in return type position, both are orthogonal.

pale whale
prisma nebula
pale whale
#

lmao i even have that in my code ijust forgot

hybrid salmon
#

impl Copy for MyPtr<_> {} I think this is too much syntax sugar

pale whale
#

how?

#

you get '_ why not just _

#

also, related: why does impl Trait in parameters for a function preclude explicit generics when calling it

prisma nebula
#

Having '_ for lifetimes and _ for types/consts is nice for visual disambiguation.

pale whale
#

i cant tell if youre agreeing with me or not

prisma nebula
#

why not just _
sounded like you wanted _ to work for all generic arguments

pale whale
#

oh no

#

oh i see how you could interpret it like that whoops

#

i think '_ should stay as is, but i think it'd be nice to allow for _ as a placeholder for generircs you dont care for

#

in a similar vein

#
struct Wrap<T>(T);

impl Debug for Wrap<impl Debug> {
  ...
}

impl Wrap<Option<_>> {
  fn is_some(&self) -> bool {
    self.0.is_some()
  }
}
``` etc
tame eagle
#

agreed, i've done this accidentally.. 3 times now i think

#

and am always disappointed when i re-learn it's invalid

pale whale
#

that'd be my big thing

#

oh, and having piece-wise borrows would be cool

#

it'd have to be done in a way that flowed well; im not sure the semantics or if it'd even be possible, but it'd sure as heck be cool to be able to borrow different parts of a struct at the same time

wooden delta
# light ridge High security is the only good default

This feels like you've completely dismissed my point, without addressing it at all.
Care to address why there's no room for two defaults, aptly named so that it's clear which one is fast and insecure, and which is slow and secure? Like, I accept that I may be wrong but you've basically just said, "no." without adding anything of value

forest quarry
wooden delta
#

Thank you

forest quarry
#

like on my system it's even faster than some of the non-cryptographically secure RNGs :P

#

well, chacha8 is faster than pcg32

barely

#

they're both just about 2500 MB/s

wooden delta
vale fractal
#

The usual syntax strawman is fn foo(&{bar, baz}self) -> Qux

#

My interpretation thereof is that bar and baz are public aliases that can correspond to zero or more fields, such that a field is in at most one alias

forest quarry
#

how would provenance work there
is a &{bar}self allowed to be used to access fields that aren't part of self?

#

Sounds Like A Fun Problem

wooden delta
#

Hmmmm, yeah I was only thinking of the returns...

#

You may have to specify the subset that are used, and the subset that are tied to the return?

#

Like, terrible suggestion but:

fn foo(&{bar, baz}self) -> Qux<'bar>

If foo is allowed to access bar and baz, but the return only continues to borrow bar

prisma nebula
#

That's essentially the same problem that we have today,
where &mut self methods that return shared references exclusively borrow Self with that shared ereference.

#

?eval

struct Foo(u32);

impl Foo {
    fn bar(&mut self) -> &u32 {
        &self.0
    }
    
    fn baz(&self) -> &u32 {
        &self.0
    }
}

fn main(){
    let mut foo = Foo(10);
    let bar = foo.bar();
    let baz = foo.baz();
    println!("{bar} {baz}");
}
left briarBOT
#
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  --> src/main.rs:16:15
   |
15 |     let bar = foo.bar();
   |               --------- mutable borrow occurs here
16 |     let baz = foo.baz();
   |               ^^^^^^^^^ immutable borrow occurs here
17 |     println!("{bar} {baz}");
   |                --- mutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
wooden delta
#

And after calling it, while the return is borrowed, you could call something that borrows baz

forest quarry
prisma nebula
#

My expectation is that it'd still keep borrowing all the fields in the self argument otherwise.

wooden delta
#

I'm not sure I understand, possibly a poor example. Your example borrows .0 twice, so it would be illegal regardless

#

Oh wait

#

Nope, I gotcha now

#

I misunderstood your use of "exclusively"

lapis plaza
#

how error handling should work:

fn index(arr:&[u8], x:u8) -> usize {
    for i in 0..arr.len() {
        if arr[i] == x {return i}
    }
    throw "no index found";
}
fn main() {
    let arr = [1, 2, 3, 4, 5];
    let result = index#(&arr, 6);
    // #: Result<index::Err, usize>
    // !!: panic
    // %: unreachable panic
    // nothing: defined by cargo.toml (panic default) (can't be #)
    match result {
        Err(r) => {println!("ups, code error {}", r);},
        //printed: "no index found"
        Ok(r) => {println!("{}",  r);}
    }
}
#

change my mind this is better, no _unchecked functions

lucid raft
#

you misspelled yeet

vale fractal
#

Yeah, this is terrible

night cedar
#

I don't really understand

#

this is promoting unwrap() and unwrap_unchecked() to builtin syntax?

#

I mean, I guess that's fine

#

though the implicit result type is a nono

vale fractal
#
  • absolutely nothing in index's signature says it's fallible
  • strings are not good errors, be it in terms of performance or usability. Use a matchable error, at least.
  • Having magical syntax for results is unnecessary
  • unwrap is already overused enough without abbreviating it to !!.
  • what even is an "unreachable panic"?
  • it absolutely should not be the case that not doing any error handling does an implicit unwrap. unwrap is already overused enough, !! would be worse, would be worser still.
#

All of these things are bad, as far as I'm concerned. Some are worse, but none of them is better than the status quo

#

Also, how does this have any effect on the existence of *_unchecked functions?

forest quarry
#

i guess you could have an API like

trait Error {
    type Ok;
    type Error;

    fn ok(ok: Self::Ok) -> Self;
    fn err(err: Self::Error) -> Self;
}

impl<T, E> Error for Result<T, E> {
    type Ok = T;
    type Error = E;
    fn ok(ok: T) -> Self { Result::Ok(ok) }
    fn err(err: E) -> Self { Result::Err(err) }
}

fn index<E: Error<Ok = usize, Error = &'static str>>(arr:&[u8], x:u8) -> E {
    for i in 0..arr.len() {
        if arr[i] == x {return E::ok(i)}
    }

    E::err("no index found")
}
#

and then you can pass a Panic<T> or Unreachable<T> which panics / calls unreachable_unchecked in the error case

vale fractal
#

See, that's an API I can get behind

#

Still doesn't feel necessary, but at least it doesn't have all of the above issues

forest quarry
#

maybe rust should have 2 kinds of panic macros
"your fault" and "my fault" panics

#

indexing out of bounds is your fault, if i am Vec::index

#

but a vec having a capacity > len is my fault

#

(kinda)

#

set_len lets you break that

lapis plaza
#

it affects the _uncheked functions because you can use function%() instead, that is literally a lot better, you can choose which type of error do you want

#

what should i do if i fail?
close the app and warn me
or
return customized error
or
do nothing that error don't exists

its literally the better shit

high ivy
lapis plaza
#

i dont understand

#

if they are 256+ possible errors (unlikely af) they are just u16 instead u8

#

or better: trait error as you want, because it can have metadata (example: where is the error in X array) and decide if you want to panic or get error

vale fractal
vale fractal
#

None of these needs magical syntax.

vale fractal
#
enum CustomParseError {
  InvalidByte{at: usize, byte: u8},
  SomeOtherError(Foo),
}
high ivy
#

Or, a practical example, try_send on channels, which contains the value you sent if it fails

vale fractal
#

Or Box::downcast

pale whale
#

but if it was doable, it'd be cool

#

oh here's something fun i'd love to see: the ability to set the layout of types

#

for example, syntax negotiable, ```rs
struct Value(u64);

specialize Option<Value> {
None(value) if value.0 == 16
Some(value)
}

tame eagle
#

scary

fleet jungle
#

keep behaviour separate from data

prisma nebula
#

This is about data

#

Looks to me like an odd way to have manual niches for types,
such that Value(16) isn't valid, just like 0 isn't for NonZero* integer types.

lilac badger
#

String keeping the name StrBuf

lilac badger
#

And more built-in string methods in std working with Cows

lapis plaza
#

i changed a little my idea

fn index(arr:&[u8], x:u8) -> usize {
    for i in 0..arr.len() {
        if arr[i] == x {return i}
    }
    yeet 33333333u64; //just ignore this haven't sense
} //if yeet is empty return Option<>

fn main() {
    let result = index#(&[5,56,7,8],3); //Result<u64, usize>
    //now you cant just an specified error, because some functions need it
}
#

then there is no need to people make _unchecked functions, it will be automatic

#

but its too late to add it to rust

#

or maybe not?, just update the libraries.

pearl grove
#

How do you annotate the type of the error?

summer linden
#

Unchecked functions exist for when you are absolutely sure to the extent that you're fine with your stuff going completely off the rails if it does ever turn out that the assumption does not hold

#

Which are rarely, but not never, needed to get all the way to the perf level you want

#

If (When?) UB happens, the program just stopping where it is with a fault is the least of your worries

#

But the potentially risky tools are there in the bottom shelf if you really need them

#

There exist multiple ways to do some things, get used to it (some of them are even specifically shorthands that intentionally exist along with their more general counterparts)

vale fractal
vale fractal
vale fractal
vale fractal
#

Though this is getting extremely close to just being current code + the "ok-wrapping" proposal of automatically wrapping stuff you return in Ok or Err depending on its type. That one might happen.

vale fractal
lapis plaza
vale fractal
#

It panics by default
So we lose the entire point of results existing

#

No thanks.

#

The single best feature of results is that I can see them in signatures, and must handle them. A default of "oh yeah, hidden panics" is just... no.

#

Unchecked exceptions have never not been a mistake.

lapis plaza
vale fractal
#

Results advertise that you need to handle them

#

In your proposal, there is literally no way to know other than putting a # on a function and seeing if the compiler reacts to that. You lose the ability to just look at docs and know.

#

Also, if I'm understanding correctly, you also lose all of the helper methods (map, unwrap_or, and_then, map_err, ok, etc), and lose match too

#

So you'd be replacing self-documenting code and versatile and powerful error handling with unchecked exceptions, by far the worst form of error handling (ignoring what C has)

wooden delta
#

I wish there was a way to know, statically, that something cannot panic. But I figure that would be hell to actually track

tame eagle
#

you'd have to include it in the function signature, or as an attribute

#

which.. eee

#

you could alternatively have a "does panic" attribute instead

#

which is akin to java's throws ExceptionType but minus the type part

#

idk. what are the advantages?

wooden delta
#

It would probably be trivial to always propagate something that tracks whether something could panic (similar to non-const or async, anything that calls something that might panic, might also panic), but having anything meaningful would be difficult, because this doesn't account for something that never panics despite calling possibly-panicking functions.

#

I see the advantage as similar to the advantage of returning result instead of panicking: you know when there's the possibility of an error, (and ideally the cause is documented)

#

But a scheme like the ones I described seems like it would always be either useless (too many false positives) or impossible to track

wooden delta
#

Seems more the realm of dynamic types and theorem provers tbh

high ivy
#

I would love to see thin references to DSTs... somehow (I have no idea how this would work, but I want it to work)

mystic path
#

But it would only work for local

mystic path
#

I could swear there was an attribute that did that but now I can't find any reference to it, I must have literally hallucinated the docs page about it

high ivy
#

i mean, thin dst refs can't Just Work
since you need some way of prevent oob accesses

#

maybe a trait

#
unsafe trait ThinDst {
    fn size(&self) -> usize;
}
mystic path
#

I swear I remember a docs page about thin dyn trait repr that only works if the trait is local and not inplementable outside the crate

#

I'm actually going insane at this point

forest quarry
#

with a slice it can just read off the length field

#

but if you want to support a FFI compatible &CStr

#

Good Luck

#

but that's something we do want

tribal stratus
#

Except for compiler bugs, the body of a function shouldn't effect its interface

forest quarry
tribal stratus
#

Hmm, I noticed that as part of an async bug, didn't know it happened normally as well

forest quarry
#

?play warn=true

fn sync() -> impl Sized {
  42
}

fn not_sync() -> impl Sized {
  std::cell::Cell::new(42)
}

fn assert_sync<T: Sync>(_x: T){
  dbg!(std::any::type_name::<T>());
}

fn main() {
  assert_sync(sync());
  assert_sync(not_sync());
}
left briarBOT
#
error[E0277]: `Cell<i32>` cannot be shared between threads safely
  --> src/main.rs:15:15
   |
5  | fn not_sync() -> impl Sized {
   |                  ---------- within this `impl Sized`
...
15 |   assert_sync(not_sync());
   |   ----------- ^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
   |   |
   |   required by a bound introduced by this call
   |
   = help: within `impl Sized`, the trait `Sync` is not implemented for `Cell<i32>`
note: required because it appears within the type `impl Sized`
  --> src/main.rs:5:18
   |
5  | fn not_sync() -> impl Sized {
   |                  ^^^^^^^^^^
note: required by a bound in `assert_sync`
  --> src/main.rs:9:19
   |
9  | fn assert_sync<T: Sync>(_x: T){
   |                   ^^^^ required by this bound in `assert_sync`

For more information about this error, try `rustc --explain E0277`.
tribal stratus
forest quarry
#

wait what

#

ah

#

yeah there

#

assert_sync(sync()) works fine

lapis plaza
high ivy
#

unwrap is almost always the wrong thing to do with a result

#

⚠️ completely arbitrary made up numbers ahead
i'd say it's a bit like this
80% using ? (maybe with a map_err or anyhow or whatever to give context)
5% reporting the error
5% recovering from the error
3% igoring the error
2% unwrap
0% unwrap_unchecked

forest quarry
#

grepping through rustc source, there's 3414 )?s, and 2477 ).unwraps

#

soooo

#

unwraps are fine if you know it won't happen

#

like, if it shouldn't happen, don't bubble it up

#

shouldn't as in "there's a bug in my code if it happens"

vale fractal
#

It's worth mentioning unwrap is itself a "proper error mechanism" in rustc

#

It ICEs.

high ivy
#

lot's of those unwraps are option unwraps, like on next() or last()

#

and about 500 are .unwrap_* so not true unwraps

blissful depot
#

unwrap is neat for quick and dirty dev. ? loses the traceback, whereas unwrap doesn't. And all other options are more work than.

#

there are crates that solve this, so you end up doing something like my_fallible_function().backtrace()?;, but one needs to search for those in the first place; and me personally, i've searched, found, and now forgot already.

#

Oh and you need to fiddle with function return type for ? to work properly

#

what i'm saying is that "proper" error handling is more work than .unwrap()

vale fractal
#

And this brings us to anyhow and its context

high ivy
#

with anyhow it's barely more work to get these nice context traces

pearl grove
#

There's a lot of cases where you can guarantee that your option will hold a value, and in those cases it's perfectly fine to unwrap, but that's usually impossible to know with many Results, since those usually have more complicated error states

summer linden
#

Even if you will unwrap, other people might do other things, like use a fallback

#

The nice thing about Result is that it's "just" a value, which either contains the expected return, or an error

pearl grove
#

Also if you're making a library it's not true that you know you will unwrap anyway, because you know nothing of the use cases

tribal stratus
high ivy
#

almost

fading olive
#

I think it might be neat to see a version of Rust with constness by default, like we have today with immutability by default; const would be opt-out rather than opt-in. Functions could be marked as non-const with !const fn, and FFI functions would be implicitly marked as !const. That way, when you do const FOO: Type = // ..., it would check all the possible call paths of the expression, and if any of them aren't const, then it would throw a compiler error something like: ^ this call is indirectly !const. Alternatively, a compile error could be thrown when a function that calls another !const function isn't marked with !const itself, however that would be cumbersome.

tribal stratus
fading olive
#

Oh, hmmm, you're right, I didn't consider that

tribal stratus
#

If you want a rigorous !const, it would probably look something like Haskell's IO monad

fading olive
#

i'd be lying if I said I had haskell experience, lol

fleet jungle
#

even if its guaranteed it won't ever be None/Err, it is self-documenting on why i'm assuming that.