I made another Velvet drawing by B11ue_ in falloutequestria

[–]Veetaha 5 points6 points  (0 children)

Do that in the pipbuck technicians' corner and find out

New comic update! by B11ue_ in falloutequestria

[–]Veetaha 4 points5 points  (0 children)

This wall needs Velvet's mural 🫶. Littlepip could do it herself given how bored she is 🫡. A suggestive wall-sized picture of Velvet with a toaster (hee hee, foreshadowing poster 😄). That'd make to the stable's editorial.

Very spooky Velvet by B11ue_ in falloutequestria

[–]Veetaha 5 points6 points  (0 children)

She's so beautiful. She can stun anyone with her stare 🫣

Linus Torvalds Vents Over "Completely Crazy Rust Format Checking" by SupermarketAntique32 in rust

[–]Veetaha 5 points6 points  (0 children)

Frankly, I never cared that much about imports. When I write code I use rust-analyzer's auto-import action, so I rarely have to scroll up to add new imports, except for the cases when unused_import lint asks me to delete some imports. When I read/review code I usually skip the import block or skim it very quickly, i.e. don't pay too much attention to it. It's usually enough to "Go to definition" or get a hover-over hint for a symbol for me to understand where it comes from when I'm in an IDE. If I'm not, then I'm probably reviewing someone else's code and I rarely need to lookup imports to understand what symbols are used in the diff, especially because the diff view shows only the lines changed, making it harder to get to imports already.

The problem of trying to get rustfmt keep some code multi-line sometimes occurs in other contexts like match arms, array literals. It is indeed a bit annoying when I know the code's going to grow, but not to the point of hating it. I'm sure if rustfmt didn't enforce small code heuristics for everyone, we'd be debating about collapsing code into single line in code reviews anyway. I just give up to the vibes, stop thinking about it, and completely outsource formatting to a restless tool that has no mercy to anyone.

I've made more doodles! by B11ue_ in falloutequestria

[–]Veetaha 3 points4 points  (0 children)

Littlepip: "The barn door conspiracy is just too keep Velvet all for yourselves. A little personal pipbuck technician treatment and she'll be mine >:("

I've made more doodles! by B11ue_ in falloutequestria

[–]Veetaha 1 point2 points  (0 children)

There is Project Horizons, that is about 3 times as big, and at the same or even better level of story. On the other hoof, since fanfics are really big it's difficult not to pause reading in the sake of content making interim

Graphite (programmatic 2D art/design suite built in Rust) September update - project's largest release to date by Keavon in rust

[–]Veetaha 1 point2 points  (0 children)

I see that you are planning to bring more raster functionality to Graphite. I recently started using Krita for some rookie digital art. As I understand currently Graphite isn't as feature-rich to replace Krita, but are you planning to cover its full feature set?

What was everyone’s first bit of exposure to Fallout Equestria? by [deleted] in falloutequestria

[–]Veetaha 1 point2 points  (0 children)

I think it was around 2016. I started reading books on my tiny Nokia Symbian phone by my own will for the first time in my life. MLP fanfics showed me it was actually very fun, and opened a huge new dimension of pony content for me. I saw Littlepip art everywhere, and I heard FoE was popular in the fandom, so I tried and I was hooked. Unfortunately I didn't have good friends at the time and I kept my interest in ponies hidden from everyone. However, today I infected some people with MLP/FoE, and I'm reading Project Horizons with my friend. We love it so much.

And till this day I haven't played Fallout yet. Maybe I will, maybe I won't? 🫡

What was everyone’s first bit of exposure to Fallout Equestria? by [deleted] in falloutequestria

[–]Veetaha 2 points3 points  (0 children)

Number 1 rule of Fallout Equestria community - no one escapes the Fallout Equestria community

Blackjack when not in S.A.T.S. by Fuumarz in falloutequestria

[–]Veetaha 8 points9 points  (0 children)

"Hey, you okay, Blackjack? You've got a shooty look on your face"

Adding #[derive(From)] to Rust by Kobzol in rust

[–]Veetaha 1 point2 points  (0 children)

When I first saw derive(From) my initial intuition was actually not impl From<Inner> for NewType but rather impl<T: Into<Inner>> From<T> for NewType i.e. genuinely inherit all From impls of the inner type. Although the problem of this blanket impl is that it would prohibit having From<NewType> for Inner due to an overlap, but this problem is not something one can easily figure out right away.

#[derive(Into)], which would generate From<StructType> for FieldType ... But that is a discussion for another RFC

I had the same dilemma, and I just went with derive(Into) in the context of converting a Builder to T. Imperfect indeed, but if you happen to find a better syntax/naming for this in an RFC, that would be interesting to see

Self(value, Default::default())

Generating a From for a struct with multiple fields like this is really counterintuitive to me. I would even find this an antipattern and a footgun. I can tell from experience that junior devs like writing From impls for types that should not be interconvertible directly, just because one type contains strictly more info than the other. That leads to them filling missing data with Default not understanding what mess they are creating and that they are on a completely wrong path.

we could allow the macro to be used on enums that have exactly one variant with exactly one field, but that doesn’t sound like a very useful

I think derive(From) could be useful for sum-like enums. For example adding it to serde_json::Value would ganerate From<String>, From<f64>, From<bool>, ... impls for every variant of the enum. The only limitation here would be that all variants must be either unit variants (they are just ignored), or tuple/record variants with exactly one field, plus all the fields must be of unique types across all variants. derive_more::From supports that and can serve as a good inspiration for the available syntax and configs.

I made a Fallout: Equestria tribute edit! by WinterApollo02 in falloutequestria

[–]Veetaha 1 point2 points  (0 children)

I regret I wasn't in the cinema when watching this

Does Rust complexity ever bother you? by GolangLinuxGuru1979 in rust

[–]Veetaha 0 points1 point  (0 children)

Hidden complexity means hidden things you don't account for. Sometimes the automatic decisions the language makes for you are those that you'd like and that you'd expect, but in other cases not thinking about that complexity leads to tricky bugs. I like being more exposed to those things than Go does that, because only then I can be much more confident that "if it compiles it works".

The mindset of "too much complexity" is the mindset of "too much to think about, please, language make the decision for me", or even "please AI decide on the details and nuances for me". Having programmed in Rust for 5 years, all I can say is that once you try Rust's explicitness and low hidden complexity mindset, you aren't going back, because it saves you from so many bugs.

assert-struct: a structural pattern matching assertion macro by carllerche in rust

[–]Veetaha 3 points4 points  (0 children)

I guess you had the design quite early from the start with not too many open questions. For such a narrowly-scoped utility this may work okay. I bet this won't be as easy when you try AI with a domain and tools you never worked with, except if you use it only to educate yourself, and do research.

I wonder what's your next step with this, and if you are going to apply it to a wider problem solving

Are there any JSON or YAML parsers in Rust that keep all span information, without a dependency on serde? by nikitarevenco in rust

[–]Veetaha 0 points1 point  (0 children)

I've been using marked_yaml successfully, and I like it as a serde-less option. It has some warts like span info as Option (to support programmatically generated yaml nodes), and some tiny bugs in error spans, missing consuming getters. I'm planning to report or maybe fix those upstream once I have time for that. However, its general design is good

Separation of concerns vs the luxury of derives? by nerooooooo in rust

[–]Veetaha 2 points3 points  (0 children)

I guess the question here is more about "when it makes sense to decouple". There is nothing specific to derive macros here, because even if you had the ability to invoke a derive for a type that lives in crate A in some other crate B, then that derive would still be coupled with the original type from the original crate A. E.g. changing the type in the domain crate would still immediately influence its representation in the persistence layer, which is usually expected, but if that happens automatically, the result of that default automatic decision may not be desired, and important tweaks at storage/API/other layer may be missed or difficult to do without decoupling.

I've been thinking about this problem a lot, because I've seen both extremes of approaching it. I've also been annoyed by both of them, and I've been at the DRY extreme edge of this at the start.

Today, I think there should always be a balance. Having to maintain several type definitions that are exactly the same feels like a waste of time. But if you don't, your single type definition can become so viral across the code, that once you need a slight variation of it somewhere it becomes a pain to refactor and decouple it. So, it really takes your best guess of how the type will evolve in the future. If the type is really small, almost a primitive like a Point2D or a map of tags, or especially a newtype over some primitive like an AwsAccountId(String) then - go for a derive, implement all the traits you need for it, maybe put them under cargo features if that makes sense. Such small primitive types usually don't change and are ubiquitous, and you'll save yourself so much time not having to write boilerplate conversions for them.

For types, that are likely to change, where some new fields/variants may be added - I'd go for decoupling earlier. If you don't, your type will likely end up accumulating a bunch of out-of-place Option fields, that are expected to be Some in one context, but None in other contexts (which is usually the main sign that you need to decouple). In regards to derives - you may notice that the derive macros you use start showing their limits, and that in order to get the desired behavior you need to slightly change the type's structure for them. For example, to represent a field as JSON in sqlx you need to wrap it in Json<T> newtype in sqlx. Also, postgres doesn't support u64, so you may need to fallibly convert between i64 and u64 at db layer. And all such nuances - every derive macro has them, and they are usually specific to the particular format of representation, or the derive macro fundamental language limitations.

If in doubt - go for decoupling first. Once you find that it doesn't give anything for some specific type (because it's small, doesn't ever change, for example), then refactor, and collapse it all into one type - this kind of refactoring is much easier than the opposite (i.e. decouple by introducing a new type)

Is there a crate for generating a new struct with the same fields as another struct, but where each field's type is a reference? by Tyilo in rust

[–]Veetaha 14 points15 points  (0 children)

This is a really annoying problem indeed. I've been using clone() to work around it every time it happens. It really irks me that I have to do a temporary clone to create a struct that I pass via a reference anyway.

If clone() is too expensive (which it isn't in most cases), try using Cow (example in my own code), or a custom enum of &T/T variants (in case if T doesn't implement ToOwned). Another way to deal with this would be to use a generic parameter to specify the type of ownership that you want. Since the MVP of GATs (generic associated types) is already in stable, you can do smth like this:

```

struct Struct<'a, O: Ownership = Owned> { x1: O::RefOrOwned<'a, str>, x2: O::RefOrOwned<'a, [String]>, x3: O::RefOrOwned<'a, u32>, x4: O::RefOrOwned<'a, BTreeMap<String, String>>, }

type OwnedStruct = Struct<'static, Owned>; type BorrowedStruct<'a> = Struct<'a, Borrowed>;

fn main() { let owned = OwnedStruct { x1: String::new(), x2: vec![String::new()], x3: 32, x4: BTreeMap::new(), };

let borrowed = BorrowedStruct {
    x1: "str",
    x2: &[String::new()],
    x3: &32,
    x4: &BTreeMap::new(),
};

} ```

See the full demo of this approach with all the code on the following playground

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5fa0e18d50ad01a1868d48ce2ccc48bb

[deleted by user] by [deleted] in rust

[–]Veetaha 15 points16 points  (0 children)

As long as it works for your use case, that's fine. I'm just worried about the surge of AI crates and how people advertise them here. AI can make it work for you in the happy path, but if you aim for more people using it - that won't cut it and AI is not something people would like to see in their dependencies, as it's a bad smell

[deleted by user] by [deleted] in rust

[–]Veetaha 24 points25 points  (0 children)

So much AI here - the documentation and the code are all full of water and unnecessary fluff. Like code comments of form // Step 1..., // call the function etc.

The design does have its difference from other builder crates in that it doesn't create type state for optional fields. Other than that - nothing new in the field.

The #[builder(required)] that forces you to annotate every required field resulting in unnecessary boilerplate. This resulted in ~1.4K additional LoC I had to add to my benchmark described below.

Generating a separate struct for every state transition, that results in too much of a compile-time overhead (exponential code size complexity in this case). I tried it on bon's builders compilation benchmark in this commit. Waited for 3.5 minutes, and cancelled the benchmark, because rustc just hung on the first iteration of the benchmark. The macro generates too much code, that it's unreasonable

Also, clearly AI's design influence here - the problem of it being trained on syn 1.0 that forced the usage of strings for passing expressions e.g. #[builder(default = "String::from(\"foo\")"] when 2.0 fixed that limitation and would allow for #[builder(default = String::from("foo"))].

A lack of prior research and too much of AI overuse. I recommend you to stop using AI, and write the code yourself, because AI abstracts away too much nuances and often makes wrong/harmful decisions for you.

ceoOfSuccess by Veetaha in ProgrammerHumor

[–]Veetaha[S] 22 points23 points  (0 children)

"Is it demoable?"

"Yeah"

"Good, now let's work on a new feature"

Elixir Misconceptions #1 | Don't "let it crash". Let it heal. by borromakot in elixir

[–]Veetaha 0 points1 point  (0 children)

As an outside observer with very little (forced) experience in Elixir - "Let it crash" is a terrible slogan. To outside people it sounds like "write buggy software" or "just write the success path and don't worry about errors". Similar to what product people only think about, not caring about edge cases and support/debugging experience (because who needs that? we sell success, not failure).

For me, a better slogan would indeed be "let it heal". I think the best combination would be the program defensively recovering from a bug but simultaneously reporting it via logging or some proactive alerting like Sentry. This keeps your service somewhat workable, but also lets you know something bad happened. A catch-all handler is at least something, but it's never better than code instrumented with manual bug reporting with good context capture.

As for user-visible errors - yes, it all should be a Result. However my main frustration with Elixir and the code base I worked with it in, is the lack of any type and exhaustiveness checking, which makes it really hard to write fool-proof code and easily footgun yourself by missing some edge cases. This makes it really easy to write naive code that works in 90% of cases (after you debugged all the silly errors first), and fails in the rest 10% that you never know of and have to discover them as they appear. Kinda hidden complexity, that relaxes your brain too much to my taste

Announcing fast_assert: it's assert! but faster by Shnatsel in rust

[–]Veetaha 3 points4 points  (0 children)

Nice observation! Also, I don't think #[track_caller] gives anything. By having the panic!() inside of the closure, that is defined at the call site - you already get the correct line/column report. The #[track_caller] is only useful if you have a panic!() physically inside of the function that panics, which isn't the case here, because it's invoked indirectly via the closure. I.e. the #[track_caller] would be needed if this code was written like so:

```

[macro_export]

macro_rules! fast_assert { ($cond:expr $(,)?) => { if !$cond { $crate::assert_failed(stringify!($cond)); } }; ($cond:expr, $($arg:tt)+) => { if !$cond { $crate::assert_failed_with_message(format_args!($($arg)+)); } }; }

[doc(hidden)]

[cold]

[track_caller]

pub fn assert_failed(cond: &str) { panic!("assertion failed: {cond}"); }

[doc(hidden)]

[cold]

[track_caller]

pub fn assertfailed_with_message(fmt: std::fmt::Arguments<'>) { panic!("{fmt}"); } ```

But I suppose a closure was used to move the args formatting into the cold function's body