Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Vanadium
Jan 8, 2005

Result<(), T> is a legit pattern. :colbert:

Adbot
ADBOT LOVES YOU

QuantumNinja
Mar 8, 2013

Trust me.
I pretend to be a ninja.

taqueso posted:

I don't want to panic inside my library for a minor error like this. I'm trying to pass the results all the way out to the API boundary so the calling app can see & handle the errors. I considered using Option here, but didn't use it for 2 reasons - I read somewhere that it was bad form to use None to indicate an error condition, and also so I don't have to convert the Option to a Result in the calling functions. This is pretty analogous to std's use of None when popping from a collection, so it is mostly for the second reason.

The library is pretty functional now, I'll try to post the code later today.

I agree that panic! is probably too much. But I wasn't advocating None as Error, I was advocating None as the unit result, where you only get something back if you have an error. For the sort of thing you seem to have here, though, I agree that following the Option conventions of std::Vec is probably a good best-practices guideline. :shrug:

Vanadium posted:

Result<(), T> is a legit pattern. :colbert:

You see it in std::fmt and the serialzied, and a few other places, but it's never carried very far in those APIs. taqueso's snippet sort of implies that it's going to carry a Result<(), T> through a few layers to "propagate the error", though, and that's going to induce a nasty ok_or chain all the way up the callstack.

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

Here's the library: https://github.com/jdeeny/rust-chip8
And here's the SDL2 emulator application: https://github.com/jdeeny/vipchip

The emulator app is pretty bare. cargo run examples/computer.hex will run it. It will run code from a file filled with hex data like you get from Octo or a binary file. Escape to exit.

A few weeks ago I had everything working to about the same point as this, but that time it was all one monolithic app with no tests. This is the result of trying to split it into the UI and a library to handle the core chip8 simulation task. I've gone round in circles on more than a few design decisions in the library.

I used locks on the state data that I thought the UI would want to have fast access to, and channels for the other stuff. Honestly, I just wanted to try both. It sure seems like locks will be faster, but it intrudes on the code everywhere. Curious about a more ergonomic way to do this.

I started with the Operations as classes implementing a trait, but there was a ton of boilerplate so I switched to Enums. I found out about default implementations, so maybe I should switch back. I really don't like how the enums have un-named data. Especially now that I have some operations with flags, it is really ugly.

Can I do Src/SrcKind and Dest/DestKind in a better way? Maybe I need a macro.

Known problem: SimulatorTask needs a run function that will execute instructions autonomously, without the other thread stepping it. Right now it is sort of pointless.

Vanadium
Jan 8, 2005

QuantumNinja posted:

You see it in std::fmt and the serialzied, and a few other places, but it's never carried very far in those APIs. taqueso's snippet sort of implies that it's going to carry a Result<(), T> through a few layers to "propagate the error", though, and that's going to induce a nasty ok_or chain all the way up the callstack.

Yeah that's what try!() is designed around! Putting error results in the Err variant of Result!

Linear Zoetrope
Nov 28, 2011

A hero must cook
Is the untyped Arena dead? I seem to only be able to get TypedArena on nightly.

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

Jsor posted:

Is the untyped Arena dead? I seem to only be able to get TypedArena on nightly.

Looked for the change in github - I think it got moved to the any-arena crate

https://github.com/rust-lang/rust/commit/e2ccc4f744b93f89666fe4c8828905297bb76178#diff-d83049850151ade6fa9c57a0545754ad
https://crates.io/crates/any-arena

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

I wanted a case-insensitive equivalent of tag_s! from nom, so I wrote this one. Is this efficient? Can it be done in a better way?

https://github.com/jdeeny/nom/commit/ef00efa647f77cfd4486c7db50ea2819c81f639d

Rust code:
#[macro_export]
macro_rules! tag_nocase_s (
  ($i:expr, $tag: expr) => (
    {
      let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
        $crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
      } else if $i[0..$tag.len()].chars().map(|c| (c).to_lowercase().next().unwrap_or(c))
                    .zip($tag.chars().map(|c| (c).to_lowercase().next().unwrap_or(c)))
                    .map(|(tc, ic)| tc == ic)
                    .take_while(|r| *r == true)
                    .count() == $tag.len()
      {
        $crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
      } else {
        $crate::IResult::Error($crate::Err::Position($crate::ErrorKind::TagStr, $i))
      };
      res
    }
  );
);
e: Got some help on #rust. Turns out case-insensitive comparison of unicode characters is harder than I thought - https://botbot.me/mozilla/rust/2016-08-18/?msg=71530738&page=13

taqueso fucked around with this message at 16:56 on Aug 18, 2016

Jo
Jan 24, 2005

:allears:
Soiled Meat
I'm a little unsure how to clear up this borrowing pattern. I have a Graph object and I want to modify (for the sake of memoization), but I've got a bit match happening in it.

code:
    fn get_derivative(&mut self, node_id : NodeId, wrt : &[NodeId], input_map : &HashMap<NodeId, Vec<f32>>) -> (Vec<f32>, Vec<f32>) {
...
        match self.nodes[node_id].operation { // I guess this is an immutable borrow which keeps us from doing the 'mutable borrow' below.
            Operation::MatrixMultiply(n1, n2) => {
                let (a_real, a_res) = self.get_derivative(n1, &wrt, &input_map);  // This is a mutable borrow.
Not sure how I should be rearranging my code to avoid this borrow.

Jo fucked around with this message at 06:27 on Aug 19, 2016

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
I think it's that you're trying to use self while it's borrowed (which it is because you're borrowing one of its fields). Can you pull out the node as a value whose lifetime isn't dependent on self, so that self can be un-borrowed by the time you want to call a method on it?

Jo
Jan 24, 2005

:allears:
Soiled Meat

rjmccall posted:

I think it's that you're trying to use self while it's borrowed (which it is because you're borrowing one of its fields). Can you pull out the node as a value whose lifetime isn't dependent on self, so that self can be un-borrowed by the time you want to call a method on it?

Doesn't seem like it. :( When I pulled the node as a value it just complained about moving out of the indexed context.

I guess another way of asking would be, "How the hell do I handle mutable borrows with recursion?"

EDIT: I can use separate hashmaps to memoize, but it's not a pretty solution.

Jo fucked around with this message at 06:55 on Aug 19, 2016

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
In order to appease the borrow-checker, you're going to have to prove that your recursive call can't invalidate nodes or visit the same node re-entrantly. So first you should think: how do I know that that's actually true? And then you should think: how on earth can I convince a relatively simple scope-based static analysis that that's true?

If your graph is really arbitrary, your node references are probably reference-counted (or externally owned?), and you probably have some sort of visited set to prevent re-entrance. That visited set is what gives you correctness here, and it makes sense that it should be the thing offering you a mutable reference when it proves that you're not accessing a node re-entrantly. Its internals will probably need to be unsafe to make that work.

If this is really a tree, with child nodes held with unique ownership at different places in the tree, then the basic problem is that there's no reason your arbitrary method call on self couldn't invalidate the node's children. Maybe you can just pass the index of the node down to the call instead of trying to pass a reference.

The other approach, perhaps more promising, is to try to set up your memoization caches so that they can be modified given just an immutable borrow of a node. I'm pretty sure there are library types designed for that.

syncathetic
Oct 21, 2010

Jo posted:

Doesn't seem like it. :( When I pulled the node as a value it just complained about moving out of the indexed context.

That error happens because indexing technically returns a value, but you can't move out of a reference. (There are a couple of ways to fix that particular error https://is.gd/nj33No ).

What I think rjmccall was suggesting was something like:

code:
let op = self.nodes[node_id].operation.clone();
match op {
    // ...
}
This way, self isn't borrowed during the body of the match. Obviously, this only makes sense if an operation is inexpensive to clone.

If you're memorizing, it might make sense for get_derivative to borrow self immutably and wrap the hashmap(s) in a RefCell. If the semantic value of the graph isn't changing, interior mutability may be justified.

syncathetic fucked around with this message at 07:23 on Aug 19, 2016

sarehu
Apr 20, 2007

(call/cc call/cc)
I went to a Rust meetup a couple days ago. Seven people showed.

One guy was a C guy asking questions who seemed to have shown up with no idea what Rust was for. He asked what is it for. He also asked if it had as good a prepocessor system as the C preprocessor. Another seemed to know quite a bit about Rust, and other things. Another guy spent the entire time editing some Medium post. We spent the time doing an exercism.io exercise where an object has a mutable String field.

But, as a result, I wrote my actual first self-originated line of Rust code, so there's that!

Edit: On that note, is there any way to make a match pattern redundantly declare the type of a variable? Take for example
code:
pub fn hello(name: Option<&str>) -> String {
    "Hello, ".to_string() + &match name {
        Some(s) => s.to_string(),
        None => "World".to_string()
    } + &"!".to_string()
}
I wanted to write Some(s: &str). That doesn't work. Is there any way to do that?

sarehu fucked around with this message at 08:09 on Aug 19, 2016

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

sarehu posted:

I went to a Rust meetup a couple days ago. Seven people showed.

One guy was a C guy asking questions who seemed to have shown up with no idea what Rust was for. He asked what is it for. He also asked if it had as good a prepocessor system as the C preprocessor. Another seemed to know quite a bit about Rust, and other things. Another guy spent the entire time editing some Medium post. We spent the time doing an exercism.io exercise where an object has a mutable String field.

But, as a result, I wrote my actual first self-originated line of Rust code, so there's that!

Edit: On that note, is there any way to make a match pattern redundantly declare the type of a variable? Take for example
code:
pub fn hello(name: Option<&str>) -> String {
    "Hello, ".to_string() + &match name {
        Some(s) => s.to_string(),
        None => "World".to_string()
    } + &"!".to_string()
}
I wanted to write Some(s: &str). That doesn't work. Is there any way to do that?

Where was your meetup?

I don't think you can do redundant typing inside the match pattern. At least, I couldn't make it happen the other day.

sarehu
Apr 20, 2007

(call/cc call/cc)
San Diego.

Deus Rex
Mar 5, 2005

sarehu posted:

Edit: On that note, is there any way to make a match pattern redundantly declare the type of a variable? Take for example
code:
pub fn hello(name: Option<&str>) -> String {
    "Hello, ".to_string() + &match name {
        Some(s) => s.to_string(),
        None => "World".to_string()
    } + &"!".to_string()
}
I wanted to write Some(s: &str). That doesn't work. Is there any way to do that?

The feature you're looking for is called "type ascription": https://github.com/rust-lang/rfcs/issues/354

Ghost of Reagan Past
Oct 7, 2003

rock and roll fun

taqueso posted:

Here's the library: https://github.com/jdeeny/rust-chip8
And here's the SDL2 emulator application: https://github.com/jdeeny/vipchip

The emulator app is pretty bare. cargo run examples/computer.hex will run it. It will run code from a file filled with hex data like you get from Octo or a binary file. Escape to exit.

A few weeks ago I had everything working to about the same point as this, but that time it was all one monolithic app with no tests. This is the result of trying to split it into the UI and a library to handle the core chip8 simulation task. I've gone round in circles on more than a few design decisions in the library.

I used locks on the state data that I thought the UI would want to have fast access to, and channels for the other stuff. Honestly, I just wanted to try both. It sure seems like locks will be faster, but it intrudes on the code everywhere. Curious about a more ergonomic way to do this.

I started with the Operations as classes implementing a trait, but there was a ton of boilerplate so I switched to Enums. I found out about default implementations, so maybe I should switch back. I really don't like how the enums have un-named data. Especially now that I have some operations with flags, it is really ugly.

Can I do Src/SrcKind and Dest/DestKind in a better way? Maybe I need a macro.

Known problem: SimulatorTask needs a run function that will execute instructions autonomously, without the other thread stepping it. Right now it is sort of pointless.
Cool coincidence, I decided I wanted to learn some Rust this evening and I started writing a Chip-8 emulator :haw:

I'm still trying to wrap my head around memory and all that; I'm used to Python and Haskell, and don't know any C or C++, but Rust seems neat (and this emulator project is cool; I've never done anything with systems code at all and it's very educational). I'm sure mine is a monstrosity of non-idiomatic Rust, but oh well. It reads instructions and does things, though I don't have any drawing implemented so :iiam: if it's actually doing the right things.

Can someone answer my extreme noob question? This feels like it should be valid Rust
code:
fn main() {
    let mut x: u32 = 5;
    reassign(&mut x); // type mismatch
}

fn reassign(x: &mut u32) {
    x = 4;
}
Why is it not valid Rust? x is mutable, and the function borrows a mutable x, but the docs don't explain why this isn't valid as far as I can tell. I know it works if you pass a struct and change a value on the struct, with the exact same pattern (this is what the docs have as examples), but rustc keeps telling me about a type mismatch with this.

Another question: is there a way to get Cargo to update rustc? They release a new stable every 6 weeks, and so if I want to keep my local updated, how do I do that except uninstall old versions and install the new one (Windows)?

Ghost of Reagan Past fucked around with this message at 02:24 on Aug 22, 2016

Linear Zoetrope
Nov 28, 2011

A hero must cook
This may be foreign since you're not used to languages with pointer/reference/value distinctions like C++ or Go , but since the function takes a reference to a u32, it can't accept x since it is a u32. You need to give it a reference to a u32. You can do this with the & operator.

code:
reassign(&x);
This is what the error message:

quote:

note: expected type `&mut u32`
note: found type `u32`

means.

I'm not sure why this was working with a struct, it shouldn't unless the autoreferencing rules on structs are more lenient than I thought (usually auto-(de)referencing only happens in a few places like method calls).

Ghost of Reagan Past
Oct 7, 2003

rock and roll fun
Oh I forgot to add the &mut when I passed the function in the example.

It was in the original code so I edited it above (this is just a toy example, I just ran against it earlier).

This is the error I get
code:
src\main.rs:41:9: 41:10 note: expected type `&mut u32
src\main.rs:41:9: 41:10 note:    found type `_`
My understanding is that the 'reassign' function is borrowing x, and since it's mutably borrowing it, it should be allowed to change it.

Ghost of Reagan Past fucked around with this message at 02:32 on Aug 22, 2016

Asymmetrikon
Oct 30, 2009

I believe you're a big dork!
You need to do this:

code:
fn reassign(x: &mut u32) {
    *x = 4;
}
You can't assign a u32 to a &u32, so you have to dereference it. It worked on a struct previously because "a.b" can be implicitly dereferenced to "(*a).b" if need be - this is so operations on references to structs aren't needlessly verbose, since they're very common.

Ghost of Reagan Past
Oct 7, 2003

rock and roll fun
It was complaining that I was trying to change a reference (that much I understood). I'm borrowing it via the reference, but if I want to do something to the thing, I gotta make sure I'm talking about the thing, not the reference. This makes sense now.

Linear Zoetrope
Nov 28, 2011

A hero must cook

Asymmetrikon posted:

It worked on a struct previously because "a.b" can be implicitly dereferenced to "(*a).b" if need be - this is so operations on references to structs aren't needlessly verbose, since they're very common.

Auto-(de)referencing can get really silly if you look at its unsugaring closely, I noted in a Stack Overflow answer recently that if you have the type

code:
struct MyStruct {
    v: Vec<SomeType>,
}
And you call `self.v.iter()` it actually calls (&((*self).v)).iter()

(It's even worse than this because iter() is actually implemented on the slice intrinsic type and Vec merely implements Deref<Target=[T]>, but it wasn't important to the question)

Linear Zoetrope fucked around with this message at 02:49 on Aug 22, 2016

crazysim
May 23, 2004
I AM SOOOOO GAY

sarehu posted:

I went to a Rust meetup a couple days ago. Seven people showed.

One guy was a C guy asking questions who seemed to have shown up with no idea what Rust was for. He asked what is it for. He also asked if it had as good a prepocessor system as the C preprocessor. Another seemed to know quite a bit about Rust, and other things. Another guy spent the entire time editing some Medium post. We spent the time doing an exercism.io exercise where an object has a mutable String field.

But, as a result, I wrote my actual first self-originated line of Rust code, so there's that!

Edit: On that note, is there any way to make a match pattern redundantly declare the type of a variable? Take for example
code:
pub fn hello(name: Option<&str>) -> String {
    "Hello, ".to_string() + &match name {
        Some(s) => s.to_string(),
        None => "World".to_string()
    } + &"!".to_string()
}
I wanted to write Some(s: &str). That doesn't work. Is there any way to do that?

I'll be honest, the San Diego Rust meetup is a bit more of a study group at the moment. It's a bit hard to keep the topics both digestible to newbies and neckbeards so we started doing those koan-like exercism-like things to get people off and running faster while letting the more seasoned Rust users being able to give some code review and commentary. It's something I saw from the LA Rust Meetup. Only, they split into two groups and have one be a hack night on their Stockfighter API because their group tends to be larger.

If you have some suggestions, we would be happy to hear them.

sarehu
Apr 20, 2007

(call/cc call/cc)
:catstare:

Well I'm going back, which is something I can't say about Haskell or ACCU meetups I went to. Maybe I'll sperg about the mutex API next time.

tinaun
Jun 9, 2011

                  tell me...

Ghost of Reagan Past posted:

Another question: is there a way to get Cargo to update rustc? They release a new stable every 6 weeks, and so if I want to keep my local updated, how do I do that except uninstall old versions and install the new one (Windows)?

https://github.com/rust-lang-nursery/rustup.rs/ will be the offical way to deal with this soon enough, i haven't tried it on windows yet though, but it has support for it.

Linear Zoetrope
Nov 28, 2011

A hero must cook

tinaun posted:

https://github.com/rust-lang-nursery/rustup.rs/ will be the offical way to deal with this soon enough, i haven't tried it on windows yet though, but it has support for it.

Just installed it, works fine on Windows but if you have MSVC installed you may or may not want to explicitly tell it you want the gnu compiler depending on what libraries you use. It's also better if you generally use cmd/powershell/msys2 instead of just building everything from VS Code or whatever since it's fundamentally a command line application.

Jo
Jan 24, 2005

:allears:
Soiled Meat
I want to do an SF meetup. How'd you find yours? Meetup.com?

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

Looks like they have a bay area meetup once a month or so: https://air.mozilla.org/bay-area-rust-meetup-july-2016/

sarehu
Apr 20, 2007

(call/cc call/cc)
What's the best GUI api for Rust right now? And by "best" I mean "least likely to be broken." On Linux or Windows.

Edit: Like all I want to do is paint on a window.

Edit: Got gtk-rs examples working so I'll run with that.

sarehu fucked around with this message at 00:06 on Aug 26, 2016

sarehu
Apr 20, 2007

(call/cc call/cc)
I went to another Rust meetup. The pizza got delivered early so it was cold, had to be reheated. Some dude showed up with a 1600x1200 T60p, then another dude pulled out some old 4:3 X-series tablet. I had a VPCZ1 Vaio Z, and some other guy had a Dell. A big improvement from the everybody-but-me-uses-Apple situation. crazysim forgot his laptop (self-owned!).

crazysim
May 23, 2004
I AM SOOOOO GAY
:sweatdrop:

Hmm, that reminds me. I should go and check out rustup.rs on Windows. I wonder how well some work Rust CLI utilities will fare in a MinTTY shell.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I think Rust's terminal utilities are more targeted for MSYS2 or cmd/Powershell than Cygwin.

Vanadium
Jan 8, 2005

Day 1 of rustfest was pretty cool but I'm too lazy to go back to day 2 for the workshops, rip.

Linear Zoetrope
Nov 28, 2011

A hero must cook
I've been reading that book about Rust and Linked Lists, and they give the following example distinguishing

code:
enum List {
    Empty,
    Elem(i32, Box<List>),
}
and

code:
struct Node {
    el: i32,
    next: List,
}

enum List {
    Empty,
    Elem(Box<Node>),
}
They seem to claim that only the latter can use the null pointer optimization, and that if you split the first implementation in the middle you have to write a junk node, while the second implementation gets optimized as writing a null pointer in `next`.

Is this really true? It doesn't seem like the former should be immune from a null pointer optimization, though it would be more complex to infer.

sarehu
Apr 20, 2007

(call/cc call/cc)
The former is not immune, I think the implementation is just too dumb for it. Another example is that Option<Option<bool>> could use 1 byte.

syncathetic
Oct 21, 2010
I don't think its possible for the first case to be null-pointer optimized. Consider https://is.gd/HtdE2R . What value/address does list_end return if the final Empty value isn't on the heap?

As I understand it, Option<bool> having a size of 1 byte might be a breaking change for unsafe code. It would mean that you can no longer blindly memcpy a value onto a &mut bool for fear of clobbering the Option data in the higher bits. I could be wrong though. The exact rules for what is valid unsafe code aren't exactly clear.

VikingofRock
Aug 24, 2008




syncathetic posted:

I don't think its possible for the first case to be null-pointer optimized. Consider https://is.gd/HtdE2R . What value/address does list_end return if the final Empty value isn't on the heap?

As I understand it, Option<bool> having a size of 1 byte might be a breaking change for unsafe code. It would mean that you can no longer blindly memcpy a value onto a &mut bool for fear of clobbering the Option data in the higher bits. I could be wrong though. The exact rules for what is valid unsafe code aren't exactly clear.

Maybe I'm misunderstanding something here, but I think the idea is that you would represent List::Elem as a (i32, non-null address), and List::Empty as (32-bits-of-whatever, null). That way you can avoid having another byte in each List for the enum discriminant, because you can tell whether it is List::Empty or List::Elem by the value of its second field. So to answer your question the final List::Empty value would still be on the heap and you can still take it's address, because all the null-pointer optimization does is make the representation of each List more efficient.

As for the unsafe stuff, my guess is that memcpying into non-C-like enums is undefined behavior, because the layout of non-C-like enums is undefined. But I'm not totally sure about that.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
A memcpy into a bool should be well-defined, assuming the source holds a valid bool value. Something like 2 would not be a valid bool value.

If you have a single payload, and its representation has what we call in Swift "extra inhabitants" — bit patterns that aren't considered legal values — then you can use those inhabitants for the other cases, so that representations of the payload are exactly representations of the enum. That works for both shared and exclusive access without any impositions on the ABI.

For example, if the single payload is a pointer, there are a large number of bit-representations that can be assumed to not be legal (at least in a sufficiently high-level language). The most obvious is 0, the null pointer, but it's probably reasonable to say 1, 2, etc. are also available, up to at least a page; and if the pointer has to be aligned, then everything not aligned is available, too.

If you have multiple payloads, even of the same type, that isn't good enough; you really need spare bits in the representation to store a discriminator between those cases. If the ABI for the payload type says that those spare bits have to have some expected value, then you might be in trouble.

For example, if you have an enum with two cases that are 2-byte aligned pointers, you can theoretically use the low bit to distinguish cases, but clients handed a pointer will expect those bits to be zero.

Now, if everything accessing the case knows exactly what it's working with, then you can be arbitrarily smart about these things; you just generate code to ignore/set the discrimination bits whenever you have an access. Unfortunately, Rust allows you form a reference to a payload and pass it off as a normal reference. Unless we're going to specialize everything that might get that reference — in general not possible — then we really need the reference to refer to memory that follows the normal ABI for the payload type.

If we know that we have exclusive access to the enum, then we can form this reference by clearing the discrimination bits and then putting them back when we're done. That might be inefficient, but it would work. But Rust lets you make a shared reference to the payload from a shared reference to the enum, and there could be other references to the enum, so no dice.

If the lifetime of the payload reference can be shorter than the lifetime of the enum, then we can just copy to a temporary with the bits cleared and make a reference to that. But that's not the rule in Rust, so no dice again.

You can make it part of the ABI for the payload type that you ignore and preserve spare bits. That might be a huge penalty, though. In our pointer example, we'd have to mask on every load of a reference to a pointer, and we'd have to mask when storing a value as well. For bool, we'd have to make sure we always touched exactly the low bit; we couldn't just load the bool and compare it against zero.

So probably you're stuck with using a separate tag discriminator.

I don't know how close to this ideal Rust gets. I know Swift has limits to its logic that fall well short of it.

Arcsech
Aug 5, 2008
1.12 is out, and I am irrationally excited about the new compiler errors that are actually understandable:



Thanks Elm, for kicking all the other language maintainers in the butt about this.

Adbot
ADBOT LOVES YOU

VikingofRock
Aug 24, 2008




Arcsech posted:

1.12 is out, and I am irrationally excited about the new compiler errors that are actually understandable:



Thanks Elm, for kicking all the other language maintainers in the butt about this.

Nice! I didn't even know this was in the pipeline.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply