Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perhaps custom types should use C# Record with one positional argument #54

Open
Andrepuel opened this issue Nov 22, 2023 · 5 comments
Open
Labels
enhancement New feature or request

Comments

@Andrepuel
Copy link

In uniffi documentation, it uses the following example for custom type:

pub struct Handle(i64);

Which, in Rust, is a new type.

On the generate C# code, alias is used. This does not create a new type. This type may not be named outside of the module. Perhaps custom types should use C# Record with one positional argument that matches the UDL typedef.

@arg0d
Copy link
Collaborator

arg0d commented Nov 22, 2023

I was thinking about this a lot. The problem becomes that if struct is used, then basic types have to be explicitly wrapped when interfacing with such bindings. I'm not sure if this is acceptable. E.g.

// UDL
[Custom]
typedef string Url;

// C#
struct URL {
    // pseudo
    String Value;
}
void VisitURL(URL url);

Uri uri = ..;
VisitURL(URL(uri.AbsolutePath));

What is your use case?

@Andrepuel
Copy link
Author

I have some types that I want to be opaque but I don't want to deal with pay the cost of heap. For example, UUID. For my scenario, I consider it dangerous that my function accept any string. I would like my functions to accept only the type UUID that I have provided. It is like the difference between pub struct Handle(i64); and pub struct Handle(pub i64);.

I've checked the behavior of Python, Kotlin e Swift generator and all of them are using the "typedef" behavior. So I think the the current C# behavior is more aligned with the others generators.

It is just bugging my mind that the original project did choose rust new-type to exemplify what is generated as a type-alias in the bindgen.

For now, I've chosen to implement UUID et al using a struct with a single attribute that the end user should not access directly.

@arg0d
Copy link
Collaborator

arg0d commented Nov 23, 2023

I see, I think understand now. In Rust code, custom type is declared as an opaque type alias. In foreign bindings, the same custom type is declared as a transparent type alias. I agree that it would be useful to create an opaque type alias in bindings. This issue could also be created in uniffi-rs.

For C#, using an opaque type alias as a struct would be very useful actually. External types can't be implemented right now due to the current using A = B custom type implementation. It seems like implementing custom type as a struct wrapper would solve both these issues. When writing the current implementation I was very unsure what would be the "right" use case for custom types, so I just copied what Kotlin/Swift/ does.

@Andrepuel
Copy link
Author

Would a struct with a single private attribute be the desirable way of implementing this feature?

public struct A {
    private A _this;
}

@arg0d
Copy link
Collaborator

arg0d commented Nov 27, 2023

I guess that would work, but there should be a getter to get the underlying value. As I understand your use case is a Handle, in which case its convenient for you to keep the underlying value private. But for other types of values it might be necessary to have access to underlying value. Though I can't think of any case for that. Url comes to mind, but that could be represented as C# Uri without any type alias.

@arg0d arg0d added the enhancement New feature or request label Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants