Usage of unsafe attribute

I am trying to compile my rust code into staticlib and I try to write function for use in C# programming langue. This is first time I try it.

I think I only need to export one function for C# code to use my lib, so I wrote this funtion

type Callback = extern "C" fn(u64);

#[no_mangle]
pub extern "C" fn laz2xyz(config_name: *const u8, length: usize, callback: Callback) {
    let config_name = unsafe { std::slice::from_raw_parts(config_name, length) };
    let config_name = String::from_utf8_lossy(config_name).to_string();
    let config_name = std::path::PathBuf::from(config_name);
    let xyz = ToXYZ::build(config_name).expect("Failed to initialize ToXYZ");

    xyz.run(move |progress| {
        callback(progress);
    })
    .expect("Failed to process files");
}

This function needs to get path to file and callback function. Callback function will report progress of execution to caller.

But I get this error

|
5 | #[no_mangle]
| ^^^^^^^^^ usage of unsafe attribute
|
help: wrap the attribute in unsafe(...)
|
5 | #[unsafe(no_mangle)]
| +++++++ +

But I don't understand why. I never seen #[unsafe(no_mangle)]. All code on internet I seen was using #[no_mangle]

If I change as sugested I get diffirent error

error: this public function might dereference a raw pointer but is not marked unsafe
--> src\exports\laz2xyz.rs:7:59
|
7 | let config_name = unsafe { std::slice::from_raw_parts(config_name, length) };
|

But this function is inside unsafe block? config_name is marked as problem
image

But code does compile. I am using Visual Studio Code
Can this error be false positive?

The was changed in Edition 2024.


You have to mark your function as unsafe, too, because it takes a raw pointer as input, I believe.

Edit: I played around with this a bit and found out that the error comes from the clippy::not_unsafe_ptr_arg_deref lint. That's why your program compiles, but your editor shows you the error message. You still should mark your function as unsafe though, as you can't guarantee that the pointer is valid inside of your function, your caller has to do it.

See also: not_unsafe_ptr_arg_deref false positives · Issue #3045 · rust-lang/rust-clippy · GitHub

2 Likes

Well… duh. It's just barely 2026 yet, Rust 2024 is not yet very common “on internet”.

Depends on what you mean by “false positive”: if you believe Rust should be able to magically read C# code and know that what you are doing is safe, then it could be considered “a false positive”, if you think that function that relies on safety constraints that are not part of it's public interface then you would mark it “unsafe” and write a comment that explains how that function is supposed to be used for the program to be correct.

That's the whole point of unsafe functions: unlike normal, safe, functions that can be used incorrectly and cause memory corruption (or worse) if used incorrectly.

I misunderstood error message.
Because config_name was marked I was thinking error referce to

std::slice::from_raw_parts()

as not marked as unsafe. Not that

pub extern "C" fn laz2xyz()

is not marked as unsafe.

After changing code I have ended with this

type Callback = extern "C" fn(u64);

#[unsafe(no_mangle)]
pub unsafe extern "C" fn laz2xyz(config_name: *const u8, length: usize, callback: Callback) -> u8 {
    if config_name.is_null() || length == 0 {
        return 1;
    }

    let bytes = unsafe { std::slice::from_raw_parts(config_name, length) };

    let config_str = match std::str::from_utf8(bytes) {
        Ok(s) => s.to_string(),
        Err(_) => return 2,
    };

    let path = std::path::PathBuf::from(config_str);

    let xyz = match ToXYZ::build(path) {
        Ok(xyz) => xyz,
        Err(_) => return 3,
    };

    let result = xyz.run(move |progress| {
        callback(progress);
    });

    match result {
        Ok(_) => 0,
        Err(_) => 4,
    }
}

No errors and compiles.

Got some help from AI. I have started using few days ago.
Have asked to improve my code. Had to change half of AI code, but got few good improvements.

You'll probably want CString or OsString onstead of PathBuf

Why would I want CString or OsString instead of PathBuf?
I am expecting to get Path to file here.

Sorry, I meant instead of str, because that may restrict the paths to UTF-8 unnecessarily.

I know PathBuf internally use OsString, but this is where my knowlage about OsString ends.
I looked at OsString documentation, and the only they to create OsString from &[u8] I found is

std::ffi::OsString::from_encoded_bytes_unchecked

but from reading documentation it feels to me unsafe. It also says it should be used for &[u8] made from to_encoded_bytes(). Documentation says it is trivial mater to convert String to OsString, so I will let PathBuf convert String to OsString internally.

At last for now, as I don't know that kind of *u8 I will get in C#.

It's designed to support platform-specific filenames. On POSIX platform you use from_vec, on Windows platform you use from_wide.

Pletforms themselves have different (and incompatible!) requirements for the filenames thus there are, obviously, no universal way to support everything.

Obviously. On Windows. On Unix every sequence of non-zero bytes may be a filename, Windows is different.

That's the core issue. You would need to find out how C# deal with limitations of different platforms before you'll be able to design the proper way to handle the difference.

Using from_utf8 may or may not be a good idea depending on what C# does on Windows.

Raw pointer params don't require unsafe (only deref of the raw pointer does), but maybe the unsafe attr requires unsafe?

No. You missed this:

It wasn't error, it was warning. And it wars precisely about the fact that unchecked dereference of pointer in the fn is bad idea if function is not marked as unsafe.

1 Like

You're right, I missed it, thanks.