Code Monkey home page Code Monkey logo

rust-blog's People

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  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

rust-blog's Issues

Misconception discussion: T is a superset of both &T and &mut T

While it's true and can be presented with generics, maybe a note that the following is possible would be beneficial, so nobody erroneously extrapolates the rule:

struct Foo();
trait Trait {}
impl Trait for Foo {}
impl Trait for &Foo {}
impl Trait for &mut Foo {}

Like with impl Read for TcpStream and impl Read for &TcpStream.

Question about leaking with static refs

I'm trying to understand how leaking works in one of the &'static examples.

It's possible to generate random dynamically allocated data at run-time and return 'static references to it at the cost of leaking memory.

use rand;

// generate random 'static str refs at run-time
fn rand_str_generator() -> &'static str {
    let rand_string = rand::random::<u64>().to_string();
    Box::leak(rand_string.into_boxed_str())
}

Part of my confusion comes from the Box::leak method. The documentation says that it returns a reference to the boxed value. If a reference is returned, that means the data is still there and the program hasn't "forgotten" about it. Maybe I'm misunderstanding what it means to "leak" memory. Can you help me understand?

Request for additional example in closure/function lifetime elision distinction

From here: https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#10-closures-follow-the-same-lifetime-elision-rules-as-functions

There's no good reason for this discrepancy. Closures were first implemented with different type inference semantics than functions and now we're stuck with it forever because to unify them at this point would be a breaking change.

Would it be a breaking change? I'm struggling to come up with a example of a closure that currently compiles that would no longer compile if the elision rules were changed to match functions. A one-line example of such a closure would be illustrative and unobtrusive here. And in the event that the claim is mistaken, that would bode well for changing the language to remove this discrepancy.

Confuse: lifetime with struct and self.

https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md

#[derive(Debug)]
struct NumRef<'a>(&'a i32);

impl<'a> NumRef<'a> {
    // no more 'a on mut self
    fn some_method(&mut self) {}

    // above line desugars to
    fn some_method_desugared<'b>(&'b mut self){}
}

fn main() {
    let mut num_ref = NumRef(&5);
    num_ref.some_method();
    num_ref.some_method(); // compiles
    println!("{:?}", num_ref); // compiles
}

It seems you simply give the solution (let compiler handle the lifetime). but why?
what the lifetime 'a in struct NumRef doing/restricting? what is lifetime 'a in fn main ? (desugar lifetime in fn main maybe helpful?)

Thanks.

My benchmark results

I'm confused and wondering why are the results so different 🤔

I'm using the latest nightly, which is 1.54.0 and node 16.0.0. All tests are just for "Read".
So I tested the same code in master on my local machine, which is: Desktop Linux, i7 3GHz, 8 core (8 thread), 16GB memory 2667MHz

SA

Requests      [total, rate, throughput]         1060260, 17670.99, 17670.37
Duration      [total, attack, wait]             1m0s, 1m0s, 2.106ms
Latencies     [min, mean, 50, 90, 95, 99, max]  334.431µs, 2.219ms, 2.072ms, 2.965ms, 3.393ms, 5.35ms, 48.375ms
Bytes In      [total, mean]                     202156240, 190.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:1060260

DR

LATEST THROUGHPUT INFO
Requests      [total, rate, throughput]         588250, 9804.16, 9803.81
Duration      [total, attack, wait]             1m0s, 1m0s, 2.135ms
Latencies     [min, mean, 50, 90, 95, 99, max]  373.671µs, 3.867ms, 3.614ms, 5.928ms, 6.807ms, 8.765ms, 31.832ms
Bytes In      [total, mean]                     112159696, 190.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:588250 

PEM

LATEST THROUGHPUT INFO
Requests      [total, rate, throughput]         650226, 10837.10, 10836.52
Duration      [total, attack, wait]             1m0s, 1m0s, 3.258ms
Latencies     [min, mean, 50, 90, 95, 99, max]  421.593µs, 3.662ms, 3.206ms, 6.278ms, 7.571ms, 10.887ms, 135.706ms
Bytes In      [total, mean]                     124626650, 191.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:650226  

I ran it also on another machine, Laptop Linux, i9 2.4GHz, 8 core (16 thread), 32GB memory 3200MHz, and faced completely different result:

SA

LATEST THROUGHPUT INFO
Requests      [total, rate, throughput]         1101823, 18363.66, 18363.24
Duration      [total, attack, wait]             1m0s, 1m0s, 1.366ms
Latencies     [min, mean, 50, 90, 95, 99, max]  365.511µs, 2.101ms, 1.935ms, 2.995ms, 3.492ms, 4.689ms, 43.751ms
Bytes In      [total, mean]                     210080948, 190.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:1101823

DR

LATEST THROUGHPUT INFO
Requests      [total, rate, throughput]         218746, 3645.42, 3645.34
Duration      [total, attack, wait]             1m0s, 1m0s, 1.195ms
Latencies     [min, mean, 50, 90, 95, 99, max]  536.327µs, 10.683ms, 10.52ms, 20.746ms, 24.02ms, 29.712ms, 136.249ms
Bytes In      [total, mean]                     41707600, 190.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:218746

PEM

LATEST THROUGHPUT INFO
Requests      [total, rate, throughput]         656688, 10944.79, 10944.22
Duration      [total, attack, wait]             1m0s, 1m0s, 3.115ms
Latencies     [min, mean, 50, 90, 95, 99, max]  553.575µs, 3.541ms, 3.302ms, 5.437ms, 6.413ms, 9.094ms, 162.15ms
Bytes In      [total, mean]                     125865200, 191.67
Bytes Out     [total, mean]                     0, 0.00
Success       [ratio]                           100.00%
Status Codes  [code:count]                      200:656688

Now I have two questions:
1- Why is it completely different from what we have here in the blog post?
2- Why DR is so much different on these two machines?! (I ran them several times, it's almost the same)

PartialEq example isn't right

The "Kelvin / Celcius / Fahrenheit" example for PartialEq isn't right, as floating point numbers suck.

I expected to break transitivity, but it turns out even symmetry is broken fairly easily, see for example:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=754a08485eb182e995f3e9889243dd81

To fix, while much less fun, maybe do millimeters, centimeters, kilometers? If they are each stored as i32, you could make the millimeters in i64 for comparison, which deals with overflow.

Paragraph about `T: 'static` is not right

TL;DR The misconception that if T: 'static then T must be valid for the entire program ... is not a misconception. It's literally true, in a sort of pedantic way.

The problem is that people conflate validity of types with lifetime of values.
The fact that a type is valid for an entire duration of a program is disctinct from whether or not there are values of that type physically existing at some point or another.

A type T: 'static is valid even before you create any values of it, in fact, it's valid even if it's absolutely impossible to create any values of it, such as with the ! or Infallible or similar types.
On the other hand, a non-static type can only be referred to in some part of a program where the type contains some specific lifetime, which only exists in that part of the prorgam. Again, this is distinct from any actual values of that type - those might not exist either.

As a consequence, leaking is completely unrelated to whether a type is 'static or not, since leaking deals with values. You can leak values of non-static types just fine. I'm afraid mentioning leaking in that section only increases the confusion.

Here's a demonstration of this:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3615f118612c24891f44e2e0c820d3a5

Hopefully this'll make sense. It's kind of hard to show this in an actual code, since I don't think there's a way of implementing functions for a non-static type only (there's no non-static marker trait etc.).

subjective issue, idk what title to give

Hey, I just started reading the too many brainfuck compilers article and it is very interesting, however, I immediately noticed a code smell I would instantly refactor - it is subjective and I may be wrong by making a whole issue about it, but here it is:

So you have a code that looks like:

pub enum Inst {
    IncPtr(usize),
    DecPtr(usize),
    IncByte(usize),
    DecByte(usize),
    WriteByte(usize),
    ReadByte(usize),
    LoopStart(usize, usize),
    LoopEnd(usize, usize),
}

Then you spend extra 3 lines of the article documenting explaining what are these usizes for, but this can be easily made into a self-documented and much more readable code (and at places where you use it too).
For example (with compact formatting here in the issue):

pub enum Inst {
    IncPtr { by: usize },
    DecPtr { by: usize },
    IncByte { by: usize },
    DecByte { by: usize },
    WriteByte { times: usize },
    ReadByte { times: usize },
    LoopStart { times: usize, end_idx: usize },
    LoopEnd { times: usize, start_idx: usize },
}

The run-length encoding is way too repetitive and by definition is in each enum variant, so it can be abstracted away in a struct:

pub struct Inst {
    pub kind: InstKind,
    pub times: usize,
}

pub enum InstKind {
    IncPtr,
    DecPtr,
    IncByte,
    DecByte,
    WriteByte,
    ReadByte,
    LoopStart { end_idx: usize },
    LoopEnd { start_idx: usize },
}

This improves readability and usability by a lot with no downsides (not like there is or will be 7 layers of structs/enums), and I honestly don't really know why tuple structs even exist at all. As I've said, this is subjective, feel free to ignore this, it just immediately tingled me when I was reading your article (didn't even read past this yet, but it seems very-very interesting).

Mention that Read & Write aren't in the prelude when talking about Generic blanket impls

First off, amazing article! Thanks so much for writing it, it looks like a great reference and I've learnt way more than I probabaly need to know 😅.

In the Read & Write section you mention the Generic blanket impls. You already mention this in the Scope section, but I think it's worth reiterating that Read & Write aren't in the prelude so if you want to use these blanket impls you need to bring them into scope. I think it's a common enough gottcha that a lot of people would be thankfull to be reminded.

Clarify a license for blog posts themselves

Quite self-explanatory title. I want to translate one of your blog posts and publish it on a IT platform, namely habr.com, however, I am not sure if I am allowed to do so. Here are the relevant parts of Habr user agreement at the moment (emphasis mine):

4.8. By accepting the terms and conditions of this Agreement, the User shall provide Habr with a free (non-exclusive) license for using the Content in the following ways:

<...>

  • to distribute copies of the Content, i.e. to provide access to material reproduced in any material form, including through network and otherwise, as well as by selling, renting, leasing, lending, including importation for any of these purposes (right of distribution);

<...>

  • right to assign all or part of the received rights to third parties (the right to sublicense).

<...>
4.11. The User guarantees the right to use the Content under the terms and conditions of the aforementioned license to the extend required.

Since you didn't specify a license for posts themselves, you still have the exclusive rights for all your content, thereby I can't even legally publish a translation, not only on Habr but anywhere.

A potential tweak to common-rust-lifetime-misconceptions

Great post! I've needed a link for some of these before (number 11 is the most recent one I remember needed).

One nuance you might consider adding to misunderstanding 4 -- people might be conflating "generics" and "monomorphization" in their mind. So they'd be correct that it's possible to avoid monomorphization, just incorrect that it's possible to avoid generics.

More iteration trait wisdom

Here are a few extra things I wish I knew when I started messing with Rust's iteration traits, in addition to what your tour of Rust traits already contains:

  • Iterator is one of those few traits where you should really consider overloading the default implementation of some methods (size_hint, nth...), as it is suboptimal in many cases.
  • Extend is a thing and FromIterator should almost always be implemented in terms of it.

T only contains owned types - Question ?

In Misconception-1: T only contains owned types. It is mentioned that:

trait Trait {}

impl<T> Trait for T {}

impl<T> Trait for &T {} // compile error

impl<T> Trait for &mut T {} // compile error

The above is True , but why does this work for concrete types:

#[derive(Copy, Clone)]
struct X {
     val: i32,
}

 trait T {}

impl T for X {}

impl T for &X {}

impl T for &mut X {}

This compiles fine .

So concrete types are special ?. Aren't generic types monomorphised to concrete types, i am confused.

Use blog posts in the Rust wiki

According to the readme, the license applies to the code examples, but not whole blog posts, so I have to ask:

Am I allowed to copy your blog post from 2020-05-19 to the Rust wiki and quote you as the source?

Sized is not an auto trait

This section starts with "The Sized trait in Rust is an auto trait". However, the compiler disagrees.

Code:

trait Foo {}

fn foo(_: Box<dyn Foo + Sized>) {}

Output:

error[E0225]: only auto traits can be used as additional traits in a trait object
 --> src/lib.rs:3:25
  |
3 | fn foo(_: Box<dyn Foo + Sized>) {}
  |                   ---   ^^^^^ additional non-auto trait
  |                   |
  |                   first non-auto trait
  |
  = help: consider creating a new trait with all of these as super-traits and using that trait here instead: `trait NewTrait: Foo + Sized {}`
  = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>

(nit) Update description of example provided to explain the Clone trait.

In the section on Clone the description for function guarantee_length is as below:

Clone can also be useful in constructing instances of a type within a generic context. Here's an example from the previous section except using Clone instead of Default:

fn guarantee_length<T: Clone>(mut vec: Vec<T>, min_len: usize, fill_with: &T) -> Vec<T> {

But this function differs from the example in previous section in the method signature too as this takes an additional param fill_with. The signature of the function in section Default is as below:

fn guarantee_length<T: Default>(mut vec: Vec<T>, min_len: usize) -> Vec<T> {

Given this difference, I think the description for function gurantee_length in section Clone can be updated, as it differs from the previous example by taking an additional param. One way I can think of is:

Clone can also be useful in constructing instances of a type within a generic context. Here's the modified example from the previous section which makes use of Clone instead of Default:

P.S: Thank you for your excellent guides which provide a lucid explanation of many of the Rust concepts.

Remove static mut example

In the blog post about lifetimes, the following code snippet is used:

static BYTES: [u8; 3] = [1, 2, 3];
static mut MUT_BYTES: [u8; 3] = [1, 2, 3];

fn main() {
   MUT_BYTES[0] = 99; // compile error, mutating static is unsafe

    unsafe {
        MUT_BYTES[0] = 99;
        assert_eq!(99, MUT_BYTES[0]);
    }
}

There has been some controversy surrounding this feature (internals, blog), some even calling this a "misfeature" in dire need of replacement. Because they appear deceptively easy to use correctly (I think it's unproblematic here, given that indexing an array is a built-in, and not a call to index_mut which AFAIK is UB), perhaps at least a warning should be added that this is non-trivial.

Request to add extra info: cannot implement both From and TryFrom

In the TryFrom section, is it worth mentioning that you cannot implement both From and TryFrom since if From is implemented for a given type T, an infallible TryFrom is also auto-implemented for you. Therefore you end up with conflicting trait implementations for the same type. For example, let's say you implement both From and TryFrom for Point, you get the following error:

error[E0119]: conflicting implementations of trait `TryFrom<(i32, i32)>` for type `Point`
  --> src/main.rs:22:1
   |
22 | impl TryFrom<(i32, i32)> for Point {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: conflicting implementation in crate `core`:
           - impl<T, U> TryFrom<U> for T
             where U: Into<T>;

For more information about this error, try `rustc --explain E0119`.
error: could not compile `foo` due to previous error

Customer review for Rust on the eve of 2022

I would want to read your "customer's review" after 1 and half years later you started blogging about Rust.

Questions to be answered in the article from my point of view:

  • So far, how was your Rust journey?
  • Is the Rust ecosystem overperformed, underperformed, or average in terms of growth when you compare with your expectations a year ago?
  • Do you think that Rust adoption is increasing and Rust employee is on the demand?
  • What's the biggest disappointment? e.g. Some of the advanced Rust developers think that the requirement of unsafe becomes a disappointment since the sales pitch was about safety.
  • Do you remember a good problem exists in other languages that you use but not in Rust?
  • What is missing for now where you use Rust?

PS: I've met with your blog through Learning Rust in 2020 while I was looking for upper-beginner content in Rust. The article is great but some parts might be outdated as there are updates on mentioned platforms.

Struct methods can also NOT have elided lifetimes right?

The list of common misconceptions contains:

if you've ever written a struct method ... then your code has generic elided lifetime annotations all over it.

But that's not always true right?

struct A {}
impl A {
    fn method(self) -> A {
        self
    }
}
pub fn main() {
	let a = A {};
	a.method();

}

Is it worth to mention about "Polonious"?

I really enjoyed reading your Lifetimes misconceptions post, thank you! Recently I stumbled upon Polonious talk given by Niko Matsakis. So I though it would be worth to mention about it as it's related to lifetime concept.

`asymmetry` requirements in `PartialOrd`

In the blog post tour of rust standard library traits, I feel the paragraph of asymmetry requirements in partial_cmp is not right:

All PartialOrd impls must ensure that comparisons are asymmetric and transitive. That means for all a, b, and c:

  • a < b implies !(a > b) (asymmetry)
  • a < b && b < c implies a < c (transitivity)

In the document for PartialOrd, there is no part about asymmetry. The requirements for partial_cmp is:

The comparison must satisfy, for all a, b and c:

  • transitivity: a < b and b < c implies a < c. The same must hold for both == and >.
  • duality: a < b if and only if b > a.

Maybe the document is updated and we should catch up?

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.