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
VikingofRock
Aug 24, 2008




So I'll ask this thread's first stupid question. How does ownership work with arrays, vectors, etc? If I add a Foo to an array or vector, does that array gain ownership over the Foo? Hopefully it's clear what I'm asking here, if not I can try to whip up some code to clarify.

Adbot
ADBOT LOVES YOU

VikingofRock
Aug 24, 2008




Jsor posted:

Looks like it moves unless you implement copy: http://is.gd/VR7qVk

E: Note that it's considered borrowed if you box it too.

Thanks, that makes sense. Now that I think about it I think that has to be the case--otherwise stuff like this wouldn't work:

code:
struct Foo{
    pub a : u32
}

fn main (){
    let foos = [Foo{a: 1}, Foo{a: 2}, Foo{a: 3}];
    for foo in foos.iter(){
        println!("{}", foo.a);
    }
}
because the Foos would be deleted at the end of the declaration if they didn't transfer ownership. I think. I'm still trying to grok Rust's ownership/lifetime model.

VikingofRock
Aug 24, 2008




space kobold posted:

Once you get over the initial hurdle of everything bitching at you about lifetimes and learning the ~Rustic~ approach to things, like always returning just a plain bit of value and letting the caller decide if they want to box it (unique pointer to heap allocated memory), stick it on the stack, atomic reference count it, etc, things really start to click and you can appreciate the beauty of what they're trying to do with the language.

Is there a guide somewhere to doing things the ~Rustic~ way? I've made my way through the Guide and Rust By Example but I haven't really absorbed stuff like what kind of value to return.

VikingofRock
Aug 24, 2008




MALE SHOEGAZE posted:

I'm kind of offended by hello world being implemented with a macro.

To be fair, macros much better in Rust than they are in languages like C++. IMO Rust macros are sort of like C++ templates, in that they are often ugly internally but allow for seemingly magic functions that "just do" whatever a user wants them to do.

sarehu posted:

Whether a macro is complicated or the function is complicated, it's still just as complicated. It's really fruity that they don't have a non-macro version, though, but probably that would operate on bytestrings and not unicode strings, or static-lifetime string slices or whatever the gently caress a string literal is.

You could do something like

Rust code:
fn main() {
    use std::io::Write;
    std::io::stdout().write("Hello, world!".as_bytes()).unwrap();
}
if you wanted, but IMO the macro version is simpler, easier to understand, and more idiomatic, so it makes sense that they would put it in the "Hello, world!" example.

VikingofRock
Aug 24, 2008




Gul Banana posted:

Why do you need the unwrap() at the end?

You don't actually need the unwrap()--but if you don't put it there, you get a warning because write() has a tag which says to warn if you don't handle the failure case. I chose to panic in failure with unwrap(), but I suppose if I wanted a strictly equivalent program to "Hello, World!", I could do one of two things:

  1. Explicitly tell the compiler to ignore the warning by tagging the main function with #[allow(unused_must_use)]
  2. Explicitly tell the compiler to ignore the result, by writing

    Rust code:
    
    fn main() {
        use std::io::Write;
        let _ = std::io::stdout().write("Hello, world!".as_bytes()).unwrap();
    }
    

I'm pretty sure both should work, but apologies if they don't. I'm phone-posting and the Rust Playground is very hard to work with on mobile.

VikingofRock
Aug 24, 2008




So has anyone gotten rust-crypto working recently? A few months ago I was doing the matasano cryptography challenges in Rust, and I wanted to return to it, but the build for rust-crypto is failing. If no one has gotten it to work, does anyone know of any good alternatives? Nevermind, I was mistaken--I hadn't updated my Cargo.lock file. It seems to build just fine on 1.1 and 1.3 nightly.

VikingofRock fucked around with this message at 09:20 on Jul 3, 2015

VikingofRock
Aug 24, 2008




Speaking of unsafe Rust, there's a new Book in progress which covers it extensively.

VikingofRock
Aug 24, 2008




Tarion posted:

1.5 is out and 'cargo install' seems pretty cool.

Agreed. It seems like Rust tooling is really taking off, which is very exciting to me.

VikingofRock
Aug 24, 2008




Bognar posted:

Found this joke on the rust subreddit and enjoyed it:

I love it.

VikingofRock
Aug 24, 2008




Rust is officially getting some new syntactic sugar to make error handling less painful (specificially, a ? operator and a catch statement expression).

edit: fixed link, and statement -> expression

VikingofRock fucked around with this message at 23:34 on Feb 8, 2016

VikingofRock
Aug 24, 2008




Vanadium posted:

It's a catch expression thank you very much :colbert:

:doh: So it is. I've edited my post.

VikingofRock
Aug 24, 2008




gonadic io posted:

Does anybody want to critique my babby's first data structure? http://codepad.org/SG2ERyJn

There were a few things that I tried to do and failed. I know that the methods should probably be implemented in an impl with a &self parameter but I'm ignoring that for now.

I took a stab at re-writing this a little more idiomatically, which should hopefully be helpful. I would probably write the OctreeNode as this, or as this if keeping the ull indexing is important to you. I also made a more-minimally modified version of you code here, keeping the no-impl way of doing things intact.

VikingofRock
Aug 24, 2008




You might want to do something besides data structures for your first Rust project--they often have difficult ownership relations, so you'll spend a *ton* of time fighting the borrow checker, which is tough when you first start out. I've been coding in Rust for a year and a half now and I still had trouble with the borrow checker when I was coding up some of those Octree examples.

Of course, I guess you could also make the case that going straight for data structures is a good way to dive into Rust head first, in which case go for it!

VikingofRock
Aug 24, 2008




piratepilates posted:

Alright so I'm learning Rust again, and I'm trying to make a program that modifies images. I'm making a function that takes an image and returns a copy of that image that is grayscale, but I can't get it to compile yet.

...

Basically all hell breaks loose and I'm not sure what the compiler is expecting of me at this point. What am I missing to make this function work?

I took a stab at this, and I'll walk you through my thought process:

First step: the code that you gave has the following error:

quote:

src/lib.rs:10:42: 10:52 error: no method named `dimensions` found for type `image::buffer::ImageBuffer<P, C>` in the current scope
src/lib.rs:10 let (image_x, image_y) = input_image.dimensions();
^~~~~~~~~~
src/lib.rs:10:42: 10:52 note: the method `dimensions` exists but the following trait bounds were not satisfied: `C : core::ops::Deref`

Reading the note there, it seems like we were just not meeting enough of the trait bounds for `dimensions` to exist. It only exists for ImageBuffer<P, C> where P is a Pixel and C is a container which implements `Deref<Target=[P::Subpixel]>` (see http://www.piston.rs/image/image/struct.ImageBuffer.html), but we haven't specified the latter.

So now we have this:
code:
fn grayscale_image_buffer<P, C>(input_image: image::ImageBuffer<P, C>)
    -> image::ImageBuffer<image::Luma<u8>, C> where
        P: image::Pixel,
        C: std::ops::Deref<Target=[P::Subpixel]>
{
    let (image_x, image_y) = input_image.dimensions();

    image::ImageBuffer::from_fn(image_x, image_y, |x, y| {
        let pixel = input_image.get_pixel(x, y);
        let r = pixel.data[0] as f32;
        let g = pixel.data[1] as f32;
        let b = pixel.data[2] as f32;
        let average = (r + g + b) / 3.0;
        image::Luma([average as u8])
    })
}
Progress! But now we get this error:

quote:

src/lib.rs:15:17: 15:27 error: attempted access of field `data` on type `&P`, but no field with that name was found
src/lib.rs:15 let r = pixel.data[0] as f32;

Okay, so what is `data`? I can't find it under the documentation for Pixel, which makes sense because the error is saying it's not there. My guess is that you wanted to implement this for RGB images though (given your variable names), and `image::Rgb<T>` does have a field called `data`. Now this is not generic over all Pixel types: instead, we will just implement it for `image::RGB<T>`, with the appropriate bounds on `T`.

code:
fn grayscale_image_buffer<T, C>(input_image: image::ImageBuffer<image::Rgb<T>, C>)
    -> image::ImageBuffer<image::Luma<u8>, C> where
        T: image::Primitive + 'static,
        C: std::ops::Deref<Target=[T]>
{
    let (image_x, image_y) = input_image.dimensions();

    image::ImageBuffer::from_fn(image_x, image_y, |x, y| {
        let pixel = input_image.get_pixel(x, y);
        let r = pixel.data[0] as f32;
        let g = pixel.data[1] as f32;
        let b = pixel.data[2] as f32;
        let average = (r + g + b) / 3.0;
        image::Luma([average as u8])
    })
}
Now we get another error:

quote:

src/lib.rs:13:5: 20:7 error: mismatched types:
expected `image::buffer::ImageBuffer<image::color::Luma<u8>, C>`,
found `image::buffer::ImageBuffer<image::color::Luma<u8>, collections::vec::Vec<u8>>`

This is because from_fn() returns an ImageBuffer<P, Vec<P::Subpixel>>. So we need to specify this in our return type. After doing that, there are just a few more type errors to fix (you over-specified a few things). The final version looks like this:

code:
fn grayscale_image_buffer<T, C>(input_image: image::ImageBuffer<image::Rgb<T>, C>)
    -> image::ImageBuffer<image::Luma<T>, Vec<T>> where
        T: image::Primitive + 'static,
        C: std::ops::Deref<Target=[T]>
{
    let (image_x, image_y) = input_image.dimensions();

    image::ImageBuffer::from_fn(image_x, image_y, |x, y| {
        let pixel = input_image.get_pixel(x, y);
        let r = pixel.data[0];
        let g = pixel.data[1];
        let b = pixel.data[2];
        let average = (r + g + b) / T::from(3).expect("3 should be convertable to T");
        image::Luma([average])
    })
}
Hopefully this helps!

VikingofRock
Aug 24, 2008




gonadic io posted:

Is this a reasonable way to pass an optional value over a FFI boundary? Passing a (nullable) pointer and then having the C# code calling another function to free it seems like lots of pain.

code:
#[repr(C)]
pub struct BadOption<T : Sized> {
	is_some : bool,
	value : T
}
e: here's the context.

That seems pretty reasonable to me--in fact, I think that that's how an Option would be laid out if you put #[repr(C)] on it. (The piece I'm not sure about is whether it would use a bool as the discriminant.)

VikingofRock
Aug 24, 2008




gonadic io posted:

Perfect, thanks.

e: is there a better way to represent once-assignable nullable variables than Cell<Option<_>>? The type allows for re-assignation but I will never do that. I suppose I could always just put a wrapper around it.

Well if you really want to get fancy, you could do something like what hyper (an http library) does for Responses, and verify this at type level at compile time. Here's an example of what that might look like for this case.

edit: To be a bit more explicit (and maybe save you some reading), the idea is to make a Variable type, which is parameterized over a type that can be either Unassigned or Assigned. Then you implement an assign() function for Variable<Unassigned>, which consumes the Variable<Unassigned> and returns a Variable<Assigned>. Because of this consuming, you can guarantee that a Variable can be assigned at most once, and because there is no assign() function for Variable<Assigned>, you can guarantee that you can't re-assign a variable once it has been assigned.

VikingofRock fucked around with this message at 07:04 on Mar 21, 2016

VikingofRock
Aug 24, 2008




Actually now that I think about it a little more, you probably want this to work at run-time, not compile time. You can either use the above approach and Any to do run-time type checking, or you could just write a wrapper around an Option (which is a lot simpler, but loses the ability to do compile-time checking). I guess it depends on your use case (I haven't really read through your code).

VikingofRock
Aug 24, 2008




Jsor posted:

Though the immediately obvious problem that jumps out at me with this scheme is it makes loops problematic if you need to assign in those.

code:
let mut x = item::<Unassigned>::new(stuff, Unassigned)

loop {
    do_stuff();
    if some_condition() {
        x = item::<Assigned>::new(stuff, Assigned(thing));
        break;
    }
}
Won't work for typing reasons.

It's not just in loops--you can't modify x in place because when you assign you change the type. So when you modify it you need to do something like what I did in my linked example code on like 42. That will work in loops (so long as you only assign once), but yeah it's pretty awkward. So maybe writing a wrapper is nicer anyways. It is a neat trick though--I was blown away when I saw it in hyper.

edit: slightly better example of loops: http://is.gd/6i8ELg

VikingofRock fucked around with this message at 04:19 on Mar 22, 2016

VikingofRock
Aug 24, 2008




Jsor posted:

Oh yeah, you can do that. But I mentioned loops specifically because for most other constructs you can use a let rebind pattern:

code:
let x = Item::<Unassigned>::new(stuff, Unassigned)

let x = if conditional {
   x.assign(whatever)
} else {
   x.assign(something_else)
};
For instance.

I don't know if it's a good pattern, but I use it semi-frequently to create an "x prime" variable where you're deriving a value from some earlier value that's never going to be used again.

I often do something similar to "freeze" a variable. It's useful when there is some non-trivial initialization to a variable, but then the variable will not be mutated after the initialization. Like this:

code:
let mut v = Vec::new();
for i in 0..10 {
    v.push(i);
}
let v = v;
// ... do stuff with immutable v ...

VikingofRock
Aug 24, 2008




Yup, you got it (in your edit).

VikingofRock
Aug 24, 2008




gonadic io posted:

Are people happy for me to continue asking this stuff here? I don't want to dominate the thread, I guess I could go to the terrible programmers yospos thread or the rust IRC instead.

Definitely keep posting here, I'm learning a lot from thinking about the issues that you are presenting and people's responses to them. Also the Rust IRC is a fantastic resource. If you post a question here and no one answers it, you should ask IRC (and then post their response here so we can learn from it too).

VikingofRock
Aug 24, 2008




gonadic io posted:

I have lot of fixed sized arrays. Functions that return [T; 8] etc.

Working with them is a real pain since neither FromIterator nor ToIterator are implemented for them. I feel that things like map and collect should be available but it's pretty cumbersome converting between slices in order to use these functions.

Is there a recommended way for working with arrays rather than slices or should I not bother?

e: for a concrete example I have

code:
impl SubImage {
    fn split_threshold(&self) -> Option<[SubImage; 2]>
    fn quads(&self) -> Option<[SubImage; 4]>
}
and I want to implement
code:
impl SubImage {
    pub fn octs(&self) -> Option<[SubImage; 8]>
}
and the order that quads/split_threshold are called in on a SubImage doesn't matter.

I tried at first to use some flat_maps, but couldn't figure that out and instead am now trying to do it explicitly with pattern matching but still haven't managed yet. Is there a nice way to do this? Should I just use slices and forgo the compile-time guarantees?

In my experience, working with arrays in Rust totally sucks and will likely continue to suck until they add dependent typing (which is planned, but which seems somewhat far-off). For now, I'd just use slices and save yourself the headache.

VikingofRock
Aug 24, 2008




Jsor posted:

Wait, what? I thought Dependent Typing was a No Go for Rust. I mentioned it a few times in discussions on the issue tracker, and people mentioned they abandoned the idea because it wasn't a "good fit". Which is a shame because I'd really love it. Especially because first-class dependent typing isn't much work away from a full-on theorem prover which means you can prove some pretty strong guarantees for your implementations. (Unless Dependent Typing is a consequence of HKTs? I feel like people wouldn't have vehemently denied plans for dependent types if that were the case though, given that HKTs are very much in the works, though they don't think they can get it done in 2016).

Okay so I looked into this a little more, and it seems like full-on dependent typing is pretty controversial, but there is at least a push for type-level numerics (which would make arrays much nicer to work with). Here is the most recent issue about it AFAIK.

VikingofRock
Aug 24, 2008




Jsor posted:


Also, apparently (&str).to_owned() is faster than (&str).to_string() for some reason.

No longer true on nightly! Specialization allowed for this to be fixed.

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.

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.

VikingofRock
Aug 24, 2008




gonadic io posted:

Or even better I could make a macro that's invoked like:
code:
let zero = eight_segment_digit!(
  _ 
 | | 
 |_|
);
let two_point = eight_segment_digit!(
  _
  _|
 |_ o
);
:q:

Definitely do this, then post it here. This sounds awesome.

VikingofRock
Aug 24, 2008





Awesome! This will make writing a lot of small programs and example code much cleaner.

VikingofRock
Aug 24, 2008




Ralith posted:

Rust just got some really sweet enum memory layout optimizations. Highlights:
  • ignoring uninhabited variants (i.e. containing uninhabited fields), e.g.:
    • Option<!> is 0 bytes
    • Result<T, !> has the same size as T
  • using arbitrary niches, not just 0, to represent a data-less variant, e.g.:
    • Option<bool>, Option<Option<bool>>, Option<Ordering> are all 1 byte
    • Option<char> is 4 bytes
  • using a range of niches to represent multiple data-less variants, e.g.:
    • enum E { A(bool), B, C, D } is 1 byte

This is extremely cool!

VikingofRock
Aug 24, 2008




Dominoes posted:

Why do I need to explicitly declare &str in this case? Shouldn't applying & automatically do that?

&replacement could be a &str or a &String (or a &&str or a &&String etc), and if this is the only time you use replacement rustc doesn't know which one to infer it as.
Looking at the docs for the regex crate, it looks like the signature for replace_all is generic in its second parameter:

pre:
pub fn replace_all<'t, R: Replacer>(
    &self, 
    text: &'t str, 
    rep: R
) -> Cow<'t, str>
So &replacement could be any type that impls Replacer. Since right now Replacer is only impl'd for &str, NoExpand, and any FnMut(&Captures) -> String, it seems like rustc should be able to deduce that the "correct" way to interpret &replacement is as &str, but right now it doesn't do that.

I think this may be intentional, since in the future someone could theoretically go add an impl of Replacer for String (or add an impl of FnMut(&Captures) -> String for String), which would cause your code to break. So my guess is Rust purposefully makes you type out the type here for that reason.

VikingofRock
Aug 24, 2008




Walh Hara posted:

I already knew this, but this seems extremely dumb to me? Do I really need to fill the array with garbage (when I declare the variable) so I can replace this garbage in the for loop with the correct values? I was assuming there must a be better way.

Or should you just never use arrays when working with objects?

You have a few options. Off the top of my head:

  1. Fill the array with garbage, and then mutate it to the correct value. This will probably get optimized away, so it shouldn't be less efficient, although it is maybe less elegant.
  2. Use std::mem::uninitialized. This is unsafe, but if for whatever reason the above isn't getting optimized away, then this will be a little faster.
  3. Use a Vec, and collect() from an iterator over the indices. This is probably the most idiomatic way to handle this, and for a 64-element object you might as well throw it on the heap anyways. You lose the compile-time guarantee that the chess board is exactly 64 elements long, but honestly there's not too much you can do with that guarantee right now anyways because Rust doesn't have C++-style integer generics.

Personally, I'd go with the 3rd option. If you want to see what this looks like in practice, or if you want to play around with it, I wrote up some prototypes on the Rust playground.

VikingofRock
Aug 24, 2008




Dominoes posted:

Yo bros. Trying to do some matrix mult; ref the thread's title. Any wisdom from bros on how to make it compile?

Rust code:
fn project(cam: &Camera, node: &Node) -> Array1<f64> {
    // Project a 4d node onto a 2d plane.
    // [url]https://en.wikipedia.org/wiki/3D_projection[/url]

    let D_1 = array![
        [1., 0., 0.],
        [0., cam.theta[0].cos(), cam.theta[1].sin()],
        [0., -cam.theta[0].sin(), cam.theta[0].cos()]
    ];

    let D_2 = array![
        [cam.theta[1].cos(), 0., -cam.theta[1].sin()],
        [0., 1., 0.],
        [cam.theta[1].sin(), 0., cam.theta[1].cos()]
    ];

    let D_3 = array![
        [cam.theta[2].cos(), cam.theta[2].sin(), 0.],
        [-cam.theta[2].sin(), cam.theta[2].cos(), 0.],
        [0., 0., 1.]
    ];

    let D = D_1.dot(&(D_2.dot(&D_3)));

    //  d is the position of the node a in the coordinate system
    // defined by the camera, wiht origin in C and rotated by theta.
    let d = D.dot(&(node.a - &cam.c));

    let A = array![
        [1., 0., -cam.e[0] / cam.e[2], 0.],
        [0., 1., -cam.e[1] / cam.e[2], 0.],
        [0., 0., 1., 0.],
        [0., 0., -1. / cam.e[2], 1.],
    ];

    let f = A.dot(
        array![&d[0], &d[1], &d[2], 1.]
    );
[/rust]

error:
[code]
error[E0308]: mismatched types
  --> src\main.rs:68:9
   |
68 |         array![&d[0], &d[1], &d[2], 1.]
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `ndarray::ArrayBase`
   |
   = note: expected type `&_`
              found type `ndarray::ArrayBase<ndarray::OwnedRepr<&f64>, ndarray::Dim<[usize; 1]>>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for mor...
If I remove the &s around d, I get:

code:
error[E0308]: mismatched types
  --> src\main.rs:68:9
   |
68 |         array![d[0], d[1], d[2], 1.]
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `ndarray::ArrayBase`
   |
   = note: expected type `&_`
              found type `ndarray::ArrayBase<ndarray::OwnedRepr<f64>, ndarray::Dim<[usize; 1]>>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-m
Also, anyone know how to hack SA's forum code to make good code syntax highlighting? poo poo's super out of date php and doesn't support rust etc. Basically, I don't get the & stuff and can't even do basic things in Rust. I suck. It's not Haskell-monad-level 'I'm too dumb for this'; more like, I'm missing something fundamental but will eventually get it.

I don't have a lot of experience with the ndarray crate, but it looks like dot() takes its argument by reference. So try putting an & in front of the array![...] in the definition of f? If the borrow checker gives you crap about that value not living long enough, take the argument to dot() and give it its own let binding.

Whenever I get errors about the expected type of an argument, I always go and double check exactly what that argument is expecting and the type of what I'm giving it, and usually that solves it.

VikingofRock
Aug 24, 2008




Dominoes posted:

I just moved &s around rando until it worked.

Now you are programming like a true Rust expert!

e: seriously though congrats on getting it to compile! It'll (mostly) click soon, don't worry.

VikingofRock
Aug 24, 2008




This is a fun bit of obfuscated rust from reddit:

code:
fn main() {
  println!("{:?}",
    (|&__@_:&'_ _|->_{[(|(_,__,..):(_,_)|__..__)(__)]})({&({||('"');}<=*&(),&[..=..])}));
}
See if you can figure out what it prints! I got a good bit of it, but there was some syntax in there that I was unfamiliar with. I didn't realize that .. is a valid struct which can itself be the endpoint of a range.

VikingofRock
Aug 24, 2008




Dylan16807 posted:

No, "integer constant" and "floating-point constant" are their own types.

It actually makes things more consistent, because you know that (5 << 40) >> 40 is always 5.

I guess that makes some sense, but it's pretty wild to me that this
code:
func foo(x float32) {
	var result_var float32 = (x - (1.0 - 1e-5)) * 1e5
	var result_lit float32 = (1.0 - (1.0 - 1e-5)) * 1e5
	fmt.Printf("result_var: %v\n", result_var)
	fmt.Printf("result_lit: %v\n", result_lit)
}
when called with x = 1.0, prints
code:
result_var: 1.001358
result_lit: 1
(go playground link) (rust playground comparison)

VikingofRock
Aug 24, 2008




Helicity posted:

I already had imposter syndrome - I don't need a language to tell me I suck.

Just remember that memory management is hard, and even people who have been writing C for decades get it wrong. Just look at the countless buffer overflow / use after free bugs that have gotten CVEs over years. So rustc isn't telling you that you suck, it's helping you catch the mistakes that everyone makes.

VikingofRock
Aug 24, 2008




Could you just implement a ToItem trait (or AsItem, if you are returning a borrowed "view") for all of the things that you want to convert to an item? Then you could either take a ToItem in you generic function, or you could call to_item() ahead of time. (playground link)

VikingofRock
Aug 24, 2008




gonadic io posted:

From the perspective of learning, and I've taught several different colleagues from scratch, I feel like the knowledge you need for rust is basically half C++ half Haskell.

This is very accurate I think. My Rust story is that around 2015 (or so?) I was writing C++ for work and Haskell for fun, and was pining for a C++ that took some of the nicer ideas from Haskell and which had better defaults. I googled around a bit to see if such a thing existed and found the Rust beta.

VikingofRock
Aug 24, 2008




street doc posted:

I want to take an image, split it, send it to 16 threads to process, send results from that to 4 threads to process and then repeat all over again.

Rayon? Tokio? Seems like arrays are fastest, but pretty loving hard to share/message.

Rayon is definitely what you want here. The result might not even be that different from the single-threaded code.

E: if it works for your use case, I'd probably first just try doing something like image.par_chunks(image.size() / 16).reduce(empty_processed_chunk, combine_processed_chunks). Let rayon figure out the number of threads to use.

VikingofRock fucked around with this message at 05:46 on May 10, 2023

Adbot
ADBOT LOVES YOU

VikingofRock
Aug 24, 2008




Jo posted:

I'd not heard of this, but it might save me a lot of trouble. Thank you!

EDIT: One of the reasons I was almost-but-not-quite trying to roll my own ring buffer was this: the camera interface can capture to an _existing buffer_ and I was hoping to avoid allocating and pushing a new image for each frame. The existing ring buffer solutions will let a person push and pop, yes, but I didn't want to push and pop the data as much as I wanted to WRITE to the buffers and READ from them. So it was more of a... static buffer with spinning read/write heads? The difference being just whether there's an allocation/deallocation of an image. It's probably premature optimization but I've been bitten before by memory pressure when it comes to streaming video data.

It looks like rtrb's Producer and Consumer implement Write and Read, respectively, so I think it might do what you want here? Worth at least trying IMO

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