Live data from Hacker News

Comparative unsafety

flak.tedunangst.com

31–40 of 99 posts

Re: Comparative unsafety

#32
post #12

Wait, what? I don't know rust, at all, but why is path freed? I thought rust didn't have GC? > let path = CString::new(filename.as_str()).unwrap().as_ptr(); Is one of those functions calling free behind the scenes?

It's freed for the same reason it would be freed in C++.

Like C++, Rust relies heavily on RAII, in which resources are freed once they get out of scope. The "CString::new(filename.as_str()).unwrap()" returns a CString, which owns the memory for the C string. Since it's a temporary (it's not being stored anywhere), its scope ends before the next line, so the resources it owns are freed (by calling its Drop implementation) just after the ".as_ptr()" call.

One solution would be to split it into two lines:

  let path = CString::new(filename.as_str()).unwrap();
  let path = path.as_ptr();
That way, the CString would only release its resources at the end of the block.

With references, the borrow checker doesn't let you do it the wrong way; however, the borrow checker only applies to references, not to raw pointers (which is what .as_ptr() returns).

Re: Comparative unsafety

#33
post #12

Wait, what? I don't know rust, at all, but why is path freed? I thought rust didn't have GC? > let path = CString::new(filename.as_str()).unwrap().as_ptr(); Is one of those functions calling free behind the scenes?

to be clear, not path is freed, but what path points to, meaning the result of CString::new(...).unwrap()

Because it is not bound to a variable, it will get dropped before the next line, even if we create a pointer to it. if you would have borrowed instead of creating a pointer this would have been a compiler error because the borrow would have outlived the original object. But a pointer exists outside of the borrow semantics and lifetimes, that is also why it can only be used inside the unsafe block.

Re: Comparative unsafety

#34

This has the most toxic UI I've seen in my life. Switching away from my browser and back again causes the page to re-render, losing my place as it did so. Just awful!

If I try to search something within the page with Ctrl + F that also causes the whole page to re-render and display "loading ..." and "rendering ..." progress bar.

[deleted]

Re: Comparative unsafety

#35
post #23

> I wrote some rust code. Actually, he wrote some Rust and some C code wrapped in Rust. > I was writing an smtp server... the kind of smtp server you'd write in two days. (sigh) > The error would have been detected far sooner had I not been lazy and checked the return value of chown (for a file not found error). But [excused here] You can't, and mustn't avoid C error checking when writing C code, even if it's wrapped…

> I was a TA in a first-semester course in C programming for several years, and one of the harder things to inculcate is how return value checking is not optional. It's very tempting to assume your standard library function just works, always. The situation is worse than that. People check for errors except when they don't. And you are expected to know the difference. Take for instance malloc. It can return NULL but…

No one checks close()'s result because it lies.

What should you write,

  while (close(fd) != 0) {}
? Or does it risk getting an infinite loop if, e.g., some network connection disappears?

Maybe

  if (fsync(fd) == 0) 
    while (close(fd) != 0) {}
  else abort();
But maybe fsync is interruptible too.

Ultimately there is nothing strictly correct to write, and you should just not really count on files being closed before process termination, but call close() as an optimization not to leak too many fds.

Re: Comparative unsafety

#36
post #30

> Why am I using my own ffi version of chown instead of the libc crate? The libc crate prototypes chown with unsigned uid_t and gid_t types, and I want to pass -1 because I'm not interested in changing the group. I'll spare you the long rant about how one should never redeclare system interfaces if you can't take the time to do so properly, because it turns out if you dig into it, uid_t boils down to uint32_t , but e…

In Rust the simplest way would be to use the `u32::MAX` constant. Create a fun alias for it if you want to express intent.

I actually find ~0 more intuitive. u32::MAX makes me think the relevant concept is "a really big number"; ~0 makes me think the relevant concept is "a bunch of bits that are all 1s".

Re: Comparative unsafety

#37
In Rust, dereferencing a pointer is unsafe and so must be isolated within an `unsafe` block — but creating a dangling pointer is not unsafe.

What this means is that as soon as you add `unsafe` code which accepts a pointer argument, all the supposedly "safe" code which can affect this pointer argument becomes a potential source of memory errors.

To me, this was unintuitive — I expected the `unsafe` block to require an extra level of scrutiny, not the surrounding code. After making an error similar to the one described in this post, I wondered, "why isn't creating a dangling pointer unsafe?" But after following that train of thought I realized that vast amounts of code would be pulled into `unsafe` blocks as a consequence, so it's not a viable approach — and thus it became apparent why only dereferencing is unsafe.

The lesson seems to be that dereferencing requires extreme care, but I wish potential errors weren't so subtle. The `as_ptr` family of functions has been tripping people up for a long time:

https://users.rust-lang.org/t/cstring-as-ptr-is-incredibly-u...

Re: Comparative unsafety

#38
post #29

This has the most toxic UI I've seen in my life. Switching away from my browser and back again causes the page to re-render, losing my place as it did so. Just awful!

Looking at the source, the progress bar is completely fake, it really does nothing besides making you wait. It even introduces slight delays to make it look more realistic. Why? Just why? The worst part is that the site is actually very light, with none of the ads, analytics and resource hog frameworks that are all too common these days. It probably could load almost instantly if it wasn't for that fake progress bar.…

It's meant as satire on the needlessly script heavy websites that many people create nowadays. The blog post itself isn't entirely serious either.

Re: Comparative unsafety

#39
I'm somewhat wishing for a `.bind!()` macro or something similar, which will desugar `let y = x.method().bind!().reference_and_do_something()` into `let tmp = x.method(); let y = tmp.reference_and_do_something()` . This should be available for chaining for convenience.

But I see the issues (e.g. which scope should `tmp` be bound to/what is its lifetime?)

Re: Comparative unsafety

#40
post #35
post #23

Earlier quoted context omitted.

> I was a TA in a first-semester course in C programming for several years, and one of the harder things to inculcate is how return value checking is not optional. It's very tempting to assume your standard library function just works, always. The situation is worse than that. People check for errors except when they don't. And you are expected to know the difference. Take for instance malloc. It can return NULL but…

No one checks close()'s result because it lies. What should you write, while (close(fd) != 0) {} ? Or does it risk getting an infinite loop if, e.g., some network connection disappears? Maybe if (fsync(fd) == 0) while (close(fd) != 0) {} else abort(); But maybe fsync is interruptible too. Ultimately there is nothing strictly correct to write, and you should just not really count on files being closed before process t…

You cannot retry close(). Quoting from https://man7.org/linux/man-pages/man2/close.2.html#NOTES

       Retrying the close() after a failure return is the wrong thing to
       do, since this may cause a reused file descriptor from another
       thread to be closed.  This can occur because the Linux kernel
       always releases the file descriptor early in the close operation,
       freeing it for reuse; the steps that may return an error, such as
       flushing data to the filesystem or device, occur only later in
       the close operation.
That is, once you call close(), the file descriptor is always freed (even if close() returns an error), and can be reused for an open() or similar in another thread. Retrying the close() will fail with EBADF in the best case, and close something another thread has opened in the worst case.
Post reply on HN