Pre-RFC: providable


#1

Currently custom allocators and custom panic runtimes must provide their code to rustc in a different and type-unsafe way. I suggest that rust should provide the possibility to provide them with for example:

//liballoc
#[providable]
trait Allocator{
    fn allocate(size: usize) -> *mut u8;
    fn deallocate(ptr: *mut u8);
}

//myalloc
extern crate alloc;

struct MyAllocator;
impl alloc::Allocator for MyAllocator{
    // ...
}

//libstd
extern crate alloc;
fn allocate() -> &'static u16{
    unsafe{&*alloc::Allocator::allocate(2)}
}

Another example:

//crate a
#[providable]
trait A{
    fn new(x: u8) -> Self;
    fn foo(&self);
}

//crate b
extern crate a;
struct B(u8);
impl A for B{
    fn new(x: u8) -> Self{B(x)}
    fn foo(&self){println!("abc {}", self.0)}
}

//crate c
extern crate a;
extern crate b;

trait C{
    fn bar(quux: a::A);
}

struct D;

impl C for D{
    fn bar(quux: a::A){}
}

fn main(){
     let abc: Box<C> = Box::new(D::new()); //object safe
    abc.bar(a::A::new(1))
}

Functions:

//crate a
#[providable]
fn print(string: String);

//crate b
extern crate a;
#[provides(a::print)]
fn myprint(string: String){
    println!("{}", string);
}

Providables are allowed to have methods which take a self arg. They should be treaten like a trait which can only be implemented by one time.

Edit: changed providable declaration syntax and added per function providables


Refactoring std for ultimate portability
#2

AFAICT, these are basically traits? I’d much prefer to just keep the trait keyword in that case. Is the only difference that they can be implemented only once? You could say something like unique trait or api trait.


#3

I think facade trait would be the best if trait is kept in the name.


#4

Is this basically GHC’s NullaryTypeClasses? That is, a trait which doesn’t even have a Self type.


#5

If I understand it correctly, yes.


#6

Mechanisms similar to the here proposed providable have been mentioned in context of the std refactoring. There where mentioned ideas of some think like a header for a crates interface (possible automatic generated), creates with “parameters” allowing part of them to be specified out side and the singular (module?) interfaces I mentioned (kind of a more general approach to your pre-RFC).

All of them have in common that they want to help with situations of dependency inversion, like this pre-RFC


#7

Wrt. to the discussion @susurrus asked if it isn’t possible with a annotation like #[privides(item.path)] and #[providables] which I think makes a lot of sense. While the approach of having a “providable” trait makes sense for e.g. alloc, it might be to limited for some parts (where you want to abstract over platform specific code, but this platform specific code currently provides a struct/enum (e.g. through having multiple #![cfg(plattform)] branches). Additionally there is the “problem” that a additional context dependent keyword(1) is needed which not everyone likes.

(1) in addition to union, whichs RFC was recently approved if I’m not mistaken

H̶e̶r̶e̶ ̶i̶s̶ ̶s̶o̶m̶e̶ ̶s̶c̶r̶a̶t̶c̶h̶ ̶h̶o̶w̶ ̶m̶a̶y̶b̶e̶ ̶t̶h̶i̶s̶ ̶c̶o̶u̶l̶d̶ ̶b̶e̶ ̶d̶o̶n̶e̶ ̶w̶i̶t̶h̶ ̶a̶ ̶a̶n̶n̶o̶t̶a̶t̶i̶o̶n̶:̶

I just noticed hat a annotation works fine with specifing structs, enums, impl but not with specifing that e.g. some other trait has to be implemented for it. Also it won’t work with sized/unsized wrt. cases where there is a public struct where the fileds determining this are all private (aka they won’t be part of the definition marked with #![providable]).