Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The problem isn't undefined behavior per se; I was using it as an example for strncpy. Rust is a no - in fact, the goal of (safe) Rust is to eliminate undefined behavior. Zig on the other hand I don't know about.

In general, I see two issues at play here:

1. C relies heavily on unsized pointers (vs. fat pointers), which is why strncpy_s had to "break" strncpy in order to improve bounds checks.

2. strncpy memory aliasing restrictions are not encoded in the API and can only be conveyed through docs. This is a footgun.

For (1), Rust APIs of this type operate on sized slices, or in the case of strings, string slices. Zig defines strings as sized byte slices.

For (2), Rust enforces this invariant via the borrow checker by disallowing (at compile-time) a shared slice reference that points to an overlapping mutable slice reference. In other words, an API like this is simply not possible to define in (safe) Rust, which means you (as the user) do not need to pore over the docs for each stdlib function you use looking for memory-related footguns.



> For (2), Rust enforces this invariant via the borrow checker by disallowing (at compile-time) a shared slice reference that points to an overlapping mutable slice reference.

At least the last time I cared about this, the borrow checker wouldn't allow mutable and immutable borrows from the same underlying object, even if they did not overlap. (Which is more restrictive, in an obnoxious way.)


Do you mean borrows for different fields of a struct? If so, that’s handled today - it’s sometimes called “splitting borrows”: https://doc.rust-lang.org/nomicon/borrow-splitting.html


Not exactly -- independent subranges of the same range (as would be relevant to something like memcpy/memmove/strcpy). E.g.,

https://godbolt.org/z/YhGajnhEG

It's mentioned later in the same article you shared above.


  fn f() {
    let mut v = vec![1, 2, 3, 4, 5];
    let (header, tail) = v.split_at_mut(1);
    b(&header[0], &mut tail[0]);
  }


split_at_mut is just unsafe code (and sibling comment mentioned it hours before you did). The borrow checker doesn't natively understand that.


It is safe btw. The difference is that it returns two mutable references vs. one shared ref and one mutable ref. But as they noted, a mutable ref can always be “downgraded” into a shared ref.


The implementation is unsafe, as I said:

> split_at_mut is just unsafe code (and sibling comment mentioned it hours before you did). The borrow checker doesn't natively understand that.

https://doc.rust-lang.org/src/core/slice/mod.rs.html#2086


No, that’s the unchecked version. Two people are telling you that this method exists and is safe, so I am not sure why you’re still doubting this lol.


The checked variant just calls the unchecked, and the panicking variant calls the checked variant. They all need to call unsafe code. See here for details: https://doc.rust-lang.org/nomicon/borrow-splitting.html


Then you misunderstand what unsafe means in Rust. Every single Rust binary needs to eventually call unsafe code at some layer of the callstack.

Is creating a TCP socket using stdlib functions unsafe? How about writing to a file? Or acquiring a mutex?

I would suggest doing some more reading before chiming in here :)


You have totally misunderstood what the person you are talking with means by unsafe. Perhaps you should resolve that prior to such condescensions.


Indeed I have haha - my bad :)

Easier to lose context with longer comment chains...


Gotcha. There is a split_at_mut method that splits a mutable slice reference into two. That doesn’t address the problem you had, but I think that’s best you can do with safe Rust.


Yeah. It just isn't something the borrow checker natively understands.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: