These are chat archives for rust-lang/rust

21st
Feb 2019
Sean Perry
@shaleh
Feb 21 09:00
I find that using descriptive types makes refactoring fairly painless. Name the key items in the code and then try to have the functions use those types.
For instance, it is not a i32 it is a GateCode. Or UserPassword instead of String.
Matteo Ferretti
@ZER0
Feb 21 10:51
Hi all, I'm implementing a lib in Rust to be used by C, and I was wondering what is the idiomatic way to use unsafe in this case: the fuction will be used by C (that is "unsafe"), and it will pass to the rust function few pointers (that point to bytes array, fixed size). Deferencing the raw pointers in rust in unsafe, so that single operation would be wrapped in unsafeblock. But should I wrapped the whole function What is the idiomatic way?
Michal 'vorner' Vaner
@vorner
Feb 21 11:33
@ZER0 When I was doing that, I've usually done a „pure rust“ library which was safe only. Then (in a namespace, therefore separate sub-crate) I've created the C-API wrapper. In this I had only the conversions (eg. the pointers and such) so these stayed as whole-unsafe functions, but they were really really short and did only all that pointer mangling stuff. Not sure if it is idiomatic, but I simply made sure to separate the logic (where the bugs are more probable) and the thin straightforward conversion.
Zakarum
@omni-viral
Feb 21 11:41
@ZER0 When you write unsafe block around unsafe operations you must justify why it is actually safe to do
In your case it can't be done as C side can pass invalid pointers that you shouldn't dereference
Therefore whole function is unsafe
The idiomatic way for letting function be safe is if there is no way to break it for client code.
Matteo Ferretti
@ZER0
Feb 21 11:48
@omni-viral but most of the time I saw just the deferecing raw pointer they use it not to the whole functions, but just to the deferencing itself
Zakarum
@omni-viral
Feb 21 11:49
This is correct if you can guarantee pointer is actually valid
And comparing it to null is not enough
I was wondering if that is correct, or it is wrong; since I also saw in other rust materials that only the deferencing is marked as unsafe when the rest of the functions can be done "safely". But it's like I'm not totally convinced. On one side, I get it: in case you work with arrays of bytes, and you pass a pointer, the caller is responsible – as they said; the rest of operation is "safer" – also in rust when you use slice::from_raw_parts usually you mark only that call as unsafe, as far as I see.
However, if the caller is really responsible, then maybe the whole function should be marked as unsafe
even if there is only one deferencing of a fixed size byte array.
Zakarum
@omni-viral
Feb 21 11:55

if the caller is really responsible

Is exactly what unsafe function mean

Calling unsafe function caller is responsible to provide valid arguments
People tend to juse wrap whatever function or operation is unsafe into smallest unsafe block possible
But this is just plain incorrect
Matteo Ferretti
@ZER0
Feb 21 11:56
Okay, but put unsafe to the whole function doesn't means that the whole functions can be unsafe operations, and therefore if something going south in some other part of the code the compiler doesn't warn you
Zakarum
@omni-viral
Feb 21 11:57
As always, when you write code in unsafe context (inside unsafe block or function) you must be careful
That's why @vorner suggested to create Rust safe library and then just add unsafe FFI functions
This way you will only dereference pointers and do other unsafe stuff inside that wrappers and offload everything else into safe functions
Matteo Ferretti
@ZER0
Feb 21 12:00
@vorner @omni-viral do you have any links to any documentations at all that can help to made my mind? As said, I totally understand, but I still not quite convinced. I tried to search online, but there are just too many different opinions, and the official docs doesn't say much about the right way to use unsafe – It mention, of course, that a C function that Rust use is by definition unsafe, but nothing about the other way around
Zakarum
@omni-viral
Feb 21 12:01
The other way around can be totally safe. Say fn add_two(value: u32) -> u32 { value + 2 } is safe
And it can be called from C theoretically
Michal 'vorner' Vaner
@vorner
Feb 21 12:02
@ZER0 Well, if you call the function from C, then C has no notion of safety/unsafety, so that kind of is out of question. And if you call it through the C abi from another rust library (no idea why you would want to do that), then it's a „C function“, then unsafe.
Matteo Ferretti
@ZER0
Feb 21 12:02
@omni-viral sorry, I mean if you use a C function from Rust
Zakarum
@omni-viral
Feb 21 12:02
With extern "C" I guess
Ingvar Stepanyan
@RReverser
Feb 21 12:02
If you add extern then it can
heh yeah :D
Zakarum
@omni-viral
Feb 21 12:02
Any C function is inherently unsafe
Matteo Ferretti
@ZER0
Feb 21 12:02
yes, that what I said (or at least meant)
And the documentation said that too of course – however it is logical – but nothing about the Rust functions called from C.
Michal 'vorner' Vaner
@vorner
Feb 21 12:03

You can put safe code inside, though, or unsafe as you need. You're likely to get pointers so dereferencing these is unsafe, meaning the the programmer must somehow check that nobody screwed up them.

But I don't think there's a best-practice or something like that documentation. The nomicon talks about unsafe a lot.

Or you can look at the wrapper generators (there are some both ways, I don't remember the names).
Matteo Ferretti
@ZER0
Feb 21 12:05
@vorner I checked also the nomicon of course, and that makes me more confused. They've example like:
pub fn compress(src: &[u8]) -> Vec<u8> {
    unsafe {
        let srclen = src.len() as size_t;
        let psrc = src.as_ptr();

        let mut dstlen = snappy_max_compressed_length(srclen);
        let mut dst = Vec::with_capacity(dstlen as usize);
        let pdst = dst.as_mut_ptr();

        snappy_compress(psrc, srclen, pdst, &mut dstlen);
        dst.set_len(dstlen as usize);
        dst
    }
I know that it meant to be a "safe wrapper"
But still, if the whole function is unsafe, why use a block?
Ingvar Stepanyan
@RReverser
Feb 21 12:07
to mark it as safe
otherwise every caller would have to call it with unsafe
Matteo Ferretti
@ZER0
Feb 21 12:07
yes, I know, but what's the point to mark it a safe if the whole function is unsafe
Ingvar Stepanyan
@RReverser
Feb 21 12:07
but here function wraps unsafe parts in a way that it claims should be safe
Matteo Ferretti
@ZER0
Feb 21 12:07
It's like a workaround to me
Michal 'vorner' Vaner
@vorner
Feb 21 12:07
Because „hides“ the unsafe inside. For the caller, it is safe.
Ingvar Stepanyan
@RReverser
Feb 21 12:08
not really - the whole point of unsafe is to say "I know what I'm doing and with these arguments it should be fine"
Michal 'vorner' Vaner
@vorner
Feb 21 12:08
It's not a workaround. For every input the caller can provide, the function will not crash or do bad stuff ‒ it'll „tame“ the unsafety and lock it inside.
Matteo Ferretti
@ZER0
Feb 21 12:10
Okay, so it means that you know for sure that no matter what input you pass, the function – even if doing unsafe operations – is safe: but should be better, at this point, use the unsafe block only on the operation that is unsafe?
Instead the whole function, I mean.
(Sorry for all those questions, I just try to "model" a bare minimum kind of "compass" to guide me in this and future scenarios)
Ingvar Stepanyan
@RReverser
Feb 21 12:11

To show on a simpler example: if you have C function like

int get_x(int *x) { return *x; }

then calling it in Rust is indeed unsafe by default, since it can segfault if passed wrong arguments.

However, if you wrap it to accept only valid arguments, then it becomes fine and you can expose the wrapper as safe

fn get_x_safe(x: &i32) -> i32 {
  unsafe { get_x(x) }
}
Michal 'vorner' Vaner
@vorner
Feb 21 12:14
@ZER0 Well, that's more like a style/preference question then. That function contains bunch of unsafe operations, so you may want to make a big unsafe block keeping them all together ‒ either because you're lazy to write it multiple times, or to hint to the reader that these unsafe operations kind of belong together and their safety invariants are closely related.
Matteo Ferretti
@ZER0
Feb 21 12:14
Okay, so to be clear, if I export a function from Rust, that accept at least one raw pointer, that function has to be unsafe, even if the compiler / clippy doesn't bother you about – I read there was a proposal about that and was rejected.
Ingvar Stepanyan
@RReverser
Feb 21 12:15
it's not just about raw pointer, C function might do other unsafe stuff, but as long as you uphold the invariants of the other side, it's fine
oh, you said if you export a function from Rust
well, in that particular case it doesn't really matter if you mark it as unsafe or not since for FFI it will be all the same
Matteo Ferretti
@ZER0
Feb 21 12:16

So, if I have in Rust:

pub extern "C" fn foo(a_ptr: *const [c_uchar; 4])

That should be unsafe. Not just the block inside, or the deferencing. Of course as @vorner said I can just have the unsafe that does the conversion and then called the safer logic from another function.

Ingvar Stepanyan
@RReverser
Feb 21 12:16
but yeah, marking it as such doesn't hurt
Michal 'vorner' Vaner
@vorner
Feb 21 12:16
Well, if the function is going to look at the pointee (the data behind the pointer), then usually that function should logically be unsafe. If you're just going to print the value of the pointer, then not necessary
But yes, as the example I had, this was the thing:
#[no_mangle]
pub unsafe extern "C" fn pcaptain_packet(data: *const u8, len: libc::size_t, time_us: u64) -> u32 {
It doesn't really matter in practice, because it is called from C and it doesn't care if I marked it safe or unsafe, but in principle the function should be unsafe because if you don't read the docs and pass eg. wrong combination of data and len, then it'll crash on you. That shouldn't happen with a safe function no matter what you pass to it.
Matteo Ferretti
@ZER0
Feb 21 12:20
yes, exactly, my question wasn't about "in pratice", but was the "principle" behind it. Examples as librustzcash got me sideways
Thanks a lot to all of you for the talk
Denis Lisov
@tanriol
Feb 21 12:21
Sometimes even functions taking raw pointers and using them may be safe, but usually not.
Zakarum
@omni-viral
Feb 21 12:31
The most confusing part is usage of the same unsafe keyword.
unsafe { ... } means "there are unsafe op, but this is actually safe"
while unsafe fn means "this function requires caller to be careful and provide only valid input
It is only me, or it would be better if unsafe fn would still require unsafe {} blocks for unsafe operations inside it?
knewt
@knewt
Feb 21 12:34
@omni-viral well, I'd read the unsafe fn as "this function is an unsafe op, be careful when you call it"
Zakarum
@omni-viral
Feb 21 12:34
Exactly
It just matter of convenience to not require unsafe {} block inside unsafe fn because, well, you probably going to invoke a bunch of unsafe operations inside it
If all operations inside function are safe it is probably safe itself (not always though)
knewt
@knewt
Feb 21 12:38
but of course you have to be careful when calling it, or else the result will be UB
Zakarum
@omni-viral
Feb 21 12:39
Yeap
Michal 'vorner' Vaner
@vorner
Feb 21 13:36
I think it is a quite common feeling that the two unsafes should have been called differently as they mean something slightly different (or quite the opposite, depending on how you look at it), but that the train has already left the station.
Kelly Thomas Kline
@kellytk
Feb 21 15:33
Where in the book can I learn about * in examples like let foo: &String = ...; bar(*foo);?
Specifically I'd like to know if it clones behind the scenes or if it takes ownership of the reference
Tim Vermeulen
@timvermeulen
Feb 21 15:35
it’s called dereferencing, maybe that helps you find it
and it doesn’t clone anything :)
Kelly Thomas Kline
@kellytk
Feb 21 15:40
Thank you
Zakarum
@omni-viral
Feb 21 16:06
* operator dereferences references & and &mut.
And if it is called on any other type it first calls Deref::deref or DerefMut::deref_mut to get reference
Dereferencing a reference gives you the value.
But it doesn't clone under the hood
If you dereference Box it will move the value out of the box
It can also Copy the value for you
Otherwise rustc will complain that it can't move out borrowed value
Unless you just takes a reference from dereferencing result. i.e. &*foo will result in reference to whatever foo dereferencing gives you
Kelly Thomas Kline
@kellytk
Feb 21 17:10
Thanks @omni-viral
Brian Knapp
@knappador
Feb 21 21:19
I made the mistake of including a dependency below the a dependency section in the Cargo.toml. I'm on the fence about writing up an issue since I'm not clear that any behavior was incorrect or could be reasonably decided as incorrect without breaking someone else's arbitrary toml keys.
...
vulkano-win = "0.11.1"
vulkano-shaders = "0.11.1"

# Window
[dependencies.winit]
version = "0.18.1"
features = ["icon_loading"]

# Sound input
cpal = "0.8.2"  # won't throw an error but won't get cpal into the dependencies either