Giter Club home page Giter Club logo

case-studies's People

Contributors

0xflotus avatar dtolnay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

case-studies's Issues

Why not use capturing closure in Function epilogue?

// Before
fn f(&self, a: Arg1, b: Arg2) -> Ret {
    /* original function body */
}

// After
fn f(&self, a: Arg1, b: Arg2) -> Ret {
    struct Guard; //Need to complicate this type name until Macro 2.0
                          //The function body might use the same name
    impl Drop for Guard {
        fn drop(&mut self) {
            /* do the thing */
        }
    }
    let guard = Guard; //No need to change variable name
                                   //Even when the function body uses the same name

    let original_f = move || -> Ret {
        /*
         * original function body
         * Let the compiler capture self, args etc
         */
    };

    let value = original_f();

    mem::forget(guard);
    value
}

Add note about type_alias_bounds

unit-type-parameters/README.md#documentation

This section should call out that type_alias_bounds is a future compatibility lint in the sense that the compiler may enforce the bounds eventually and the current behavior is considered a compiler bug.

The thing that makes it safe and correct to write here is that the RHS of the type alias uses exactly the same bounds as the LHS. Where that isn't the case, future compiler changes may break it.

Autoref specialization doesn’t seem to work with associated types

Hey David,

Sorry if GitHub Issues isn’t the right forum for this, seemed better than reviving an old Reddit thread. I have gotten Autoref specialization to work well in toy cases, but my actual use case has an associated type in it and that causes things to fall down. As a simple (nonsensical) example:

trait MyTrait {
  type MyAssociatedType; 
}

impl<T: Clone> MyTrait for &T {
  type MyAssociatedType = bool;
}

impl MyTrait for String {
  type MyAssociatedType = str;
}

fn<T: MyTrait> get_toy_example(input: T): <T as MyTrait>::MyAssociatedType {
  ...
}

As far as I can tell the issue is simply that Autoref does not apply to the <T as MyTrait> section, so it will not find implementations for anything other than String. If I change it to <&T as MyTrait> then it will find implementations for all Clone structs but will not special-case String.

The experimental specialization feature built into Rust also falls down for associated types (and seems to be gathering dust) so I’m sort of stuck here. You seem to be the foremost expert on Rust specialization so I figured I’d ask :) Am I hosed here?

If so, might be worth updating the case study doc with that second caveat added. It does come up pretty high up on the Google results for Rust specialization.

autoref specialization vs TypeId comparisons?

Not sure if issues is an appropriate place for such discussion, but after looking at #11 thought I'd share an alternative approach I've been using.

Autoref specialization case study mentions that it works only in macros but not in generic contexts like

pub fn demo<T: ???>(value: T) -> String {
    (&value).my_to_string()
}

However, one trick I've found to work well in such contexts is comparing TypeIds, e.g.:

pub fn format<T: 'static + ?Sized + std::fmt::Debug>(value: &T) -> String {
    use std::any::TypeId;

    if TypeId::of::<T>() == TypeId::of::<[u8]>() {
        format!("{:X?}", value)
    } else {
        format!("{:?}", value)
    }
}

It has own limitations, namely it works only for 'static types due to restrictions on TypeId, but it's sometimes enough and works at least in some generic contexts.

Moreover, since the condition is comparing two constant values, optimiser is always able to remove it altogether in each monomorphised version of the function and instead unconditionally execute the correct branch, so this approach ends up being zero-cost, just like native specialization.

Recommend using match instead of let for autoref specialization

The problem with a macro definition such as:

macro_rules! anyhow {
    ($err:expr) => ({
        #[allow(unused_imports)]
        use $crate::{DisplayKind, StdErrorKind};
        let error = $err;
        (&error).anyhow_kind().new(error)
    });
}

Is that it won't work if $err relies on temporaries (e.g. it is format_args!). However, the following will work:

macro_rules! anyhow {
    ($err:expr) => ({
        #[allow(unused_imports)]
        use $crate::{DisplayKind, StdErrorKind};
        match $err {
            error => (&error).anyhow_kind().new(error),
        }
    });
}

as it's just one expression.

object oriented emulation

I haven't fully thought this through, but I think you could emulate a class hierarchy using repr[transparent] and mem::transmute on references. This hasn't been done I assume because there is no reason a sane person would do this 😋

autoref specialization vs. generic traits

I'm in the process of moving overflower to autoref specialization. I have found that it works as you described in the unary operation case (e.g. specializing std::ops::Neg vs. overflower::OverflowerNeg).

However, there is a small niggle when the trait is generic over some other type as for example std::ops::Add<RHS = Self> is over RHS. In this case, if I try to implement the kind for T: std::ops::Add, this only works for the default RHS type (which is Self, but e.g. String implements Add for various other types (e.g. &'_ str and Cow<'_, str>).

Thus we must allow our kind to be implemented for arbitrary RHS. So let's try that:

pub struct OverflowerAddTag;
pub trait OverflowerAddKind<R> {
    fn overflower_add_tag() -> OverflowerAddTag {
        OverflowerAddTag
    }
}

impl<R, T> OverflowerAddKind<R> for T where &T: std::ops::Add<R> {}
impl<R, T> OverflowerAddKind<R> for T where T: OverflowerAdd<R> {}

Now the compiler tells us that we need to give a lifetime to &T, so we do that, which gives us:

impl<'a, R, T: 'a> OverflowerAddKind<R> for T where &'a T: std::ops::Add<R> {}

This works. Just wanted to let you know.

Inherent method specialization

Thanks for writing up the autoref specialization guide!

There's a similar technique which depends on Rust favoring inherent impls over traits.

Taking the DisplayToString example:

use std::fmt::{Display, Write};

pub trait DisplayToString {
    fn my_to_string(&self) -> String;
}

pub struct DisplayToStringWrapper<'a, T: ?Sized>(pub &'a T);

// This is an inherent method, so it's tried first
impl<'a, T: AsRef<str> + ?Sized> DisplayToStringWrapper<'a, T> {
    pub fn my_to_string(&self) -> String {
        println!("called specialized impl");
        self.0.as_ref().into()
    }
}

// This is only used if the inherent method fails to type-check
impl<'a, T: Display + ?Sized> DisplayToString for DisplayToStringWrapper<'a, T> {
    fn my_to_string(&self) -> String {
        println!("blanket impl");
        self.to_string()
    }
}

This can be more reliable than the autoref approach. For example, (&&String::from("hello")).my_to_string() will fall back to the slow path using autoref specialization but stick to the fast path with inherent impls. The drawback is that it only allows for one fallback case. Overall, I think this approach makes some interesting tradeoffs and may be worth calling (hehe) out in the article!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.