profile
viewpoint
Jake Goulding shepmaster @integer32llc Pittsburgh, PA http://jakegoulding.com

carllerche/tower-web 864

A fast, boilerplate free, web framework for Rust

avr-rust/rust 485

A fork of the Rust programming language with AVR support

avr-rust/ruduino 282

Reusable components for the Arduino Uno.

carols10cents/zopfli 67

A Rust implementation of the Zopfli compression algorithm.

Gankra/hash-rs 23

Benchmarks of various hashers

apotonick/rspec-apotomo 17

Spec your widgets.

meatballhat/ansible-module-docker-pull 17

It's an Ansible module for pulling Docker containers!

avr-rust/libcore 15

[deprecated, not needed anymore] A stripped-down version of Rust's libcore that used to be required for the AVR target

issue openeddtolnay/syn

Enabling all features complains about missing feature syn-test-suite

[dependencies.syn]
package = "syn"
version = "=1.0.24"
features = ["extra-traits", "fold", "full", "test", "visit", "visit-mut"]
error: failed to select a version for `syn`.
    ... required by package `ss v0.1.0 (/private/tmp/ss)`
versions that meet the requirements `= 1.0.24` are: 1.0.24

the package `ss` depends on `syn`, with features: `syn-test-suite` but `syn` does not have these features.

This causes problems with the playground. I'll remove test for now.

created time in 41 minutes

push eventinteger32llc/rust-playground

Jake Goulding

commit sha 69abf4d29e1f2d57917577943c2de537178a17b4

Update test expectations for changed error messages

view details

Andrey Kononov

commit sha 8abd141c139584bc60fcf94089a236762989677a

Add "Macro expansion" tool

view details

Jake Goulding

commit sha a3cf77bd7f0f39de99be80c9e4e35bb4e5641644

Add integration test for macro expansion

view details

Jake Goulding

commit sha 1095e7ffc8451672f0c3237818b63ece4ee9e871

Merge pull request #599 from Flowneee/macro-expand

view details

push time in 2 hours

PR merged integer32llc/rust-playground

Add "Macro expand" tool to Playground

This PR add new tool "Macro expand" to Playground (#158). Under the hood it call cargo run -- --Zunstable-options --pretty=expanded with nightly compiler.

How it looks: image

+227 -3

3 comments

12 changed files

Flowneee

pr closed time in 2 hours

pull request commentinteger32llc/rust-playground

Add "Macro expand" tool to Playground

FWIW, I force-pushed your branch to make some small changes, but will merge and deploy soon.

Thanks!

Flowneee

comment created time in 2 hours

push eventFlowneee/rust-playground

Jake Goulding

commit sha 69abf4d29e1f2d57917577943c2de537178a17b4

Update test expectations for changed error messages

view details

Andrey Kononov

commit sha 8abd141c139584bc60fcf94089a236762989677a

Add "Macro expansion" tool

view details

Jake Goulding

commit sha a3cf77bd7f0f39de99be80c9e4e35bb4e5641644

Add integration test for macro expansion

view details

push time in 2 hours

issue commentrusoto/rusoto

s3.get_object response body is truncated when using into_blocking_read

Actually, when I try the aforementioned workaround, no progress is made:

use rusoto_core::{ByteStream, Region};
use rusoto_s3::{GetObjectRequest, S3Client, S3};
use std::{fs::File, io};
use tokio::runtime::Runtime;

type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;

const BUCKET_NAME: &str = "my bucket name";

fn main() -> Result<()> {
    let mut rt = Runtime::new()?;
    let body = rt.block_on(core())?;

    let mut body = body.into_blocking_read();
    let mut file = File::create("/tmp/a-place-to-write")?;

    io::copy(&mut body, &mut file)?;

    Ok(())
}

async fn core() -> Result<ByteStream> {
    let client = S3Client::new(Region::UsEast2);

    let mut object = client
        .get_object(GetObjectRequest {
            bucket: BUCKET_NAME.into(),
            ..Default::default()
        })
        .await?;

    let body = object.body.take().expect("The object has no body");
    Ok(body)
}

This just hangs:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
  * frame #0: 0x00007fff726e5766 libsystem_kernel.dylib`kevent + 10
    frame #1: 0x0000000100610efa dl`mio::sys::unix::kqueue::Selector::select::h57382e725b3447d3(self=0x0000000101507530, evts=0x00007ffeefbfeaf0, awakener=(__0 = 18446744073709551615), timeout=<unavailable>) at kqueue.rs:88:26
    frame #2: 0x000000010060b76f dl`mio::poll::Poll::poll2::h0ce9d8e5afef0669(self=0x0000000101507530, events=0x00007ffeefbfeaf0, timeout=Option<core::time::Duration> @ 0x00007ffeefbfc5f0, interruptible=false) at poll.rs:1178:22
    frame #3: 0x000000010060b417 dl`mio::poll::Poll::poll1::h59bd1adeadaf2e20(self=0x0000000101507530, events=0x00007ffeefbfeaf0, timeout=Option<core::time::Duration> @ 0x00007ffeefbfc698, interruptible=false) at poll.rs:1139:18
    frame #4: 0x000000010060ad5a dl`mio::poll::Poll::poll::h48e2b91fd93c786e(self=0x0000000101507530, events=0x00007ffeefbfeaf0, timeout=<unavailable>) at poll.rs:1010:8
    frame #5: 0x00000001005d92c5 dl`tokio::io::driver::Driver::turn::h0fb3749cf72bffc2(self=0x00007ffeefbfeaf0, max_wait=Option<core::time::Duration> @ 0x00007ffeefbfc858) at mod.rs:107:14
    frame #6: 0x00000001005d9bd9 dl`_$LT$tokio..io..driver..Driver$u20$as$u20$tokio..park..Park$GT$::park_timeout::h38cb46b95affd435(self=0x00007ffeefbfeaf0, duration=(secs = 28, nanos = 666186321)) at mod.rs:181:8
    frame #7: 0x000000010011a628 dl`_$LT$tokio..park..either..Either$LT$A$C$B$GT$$u20$as$u20$tokio..park..Park$GT$::park_timeout::h7a2fcc9ca2a76e21(self=0x00007ffeefbfeae8, duration=(secs = 28, nanos = 666186321)) at either.rs:35:28
    frame #8: 0x00000001001377a8 dl`_$LT$tokio..time..driver..Driver$LT$T$GT$$u20$as$u20$tokio..park..Park$GT$::park::h796f64d68963ca1b(self=0x00007ffeefbfeac0) at mod.rs:255:24
    frame #9: 0x000000010011a6bd dl`_$LT$tokio..park..either..Either$LT$A$C$B$GT$$u20$as$u20$tokio..park..Park$GT$::park::h10591ba5b8983f7b(self=0x00007ffeefbfeab8) at either.rs:28:28
    frame #10: 0x000000010000835a dl`tokio::runtime::basic_scheduler::BasicScheduler$LT$P$GT$::block_on::_$u7b$$u7b$closure$u7d$$u7d$::h66a611f250d8a802(scheduler=0x00007ffeefbfea80, context=0x00007ffeefbfd748) at basic_scheduler.rs:158:28
    frame #11: 0x0000000100008bb9 dl`tokio::runtime::basic_scheduler::enter::_$u7b$$u7b$closure$u7d$$u7d$::hd077963766603ffe at basic_scheduler.rs:213:28
    frame #12: 0x0000000100005a1b dl`tokio::macros::scoped_tls::ScopedKey$LT$T$GT$::set::he1517682a97e9f70(self=0x00000001007d75e8, t=0x00007ffeefbfd748, f=closure-0 @ 0x00007ffeefbfd890) at scoped_tls.rs:63:8
    frame #13: 0x0000000100008a8c dl`tokio::runtime::basic_scheduler::enter::h54f32e5a693bb5a3(scheduler=0x00007ffeefbfea80, f=closure-0 @ 0x00007ffeefbfdb18) at basic_scheduler.rs:213:4
    frame #14: 0x0000000100007d92 dl`tokio::runtime::basic_scheduler::BasicScheduler$LT$P$GT$::block_on::h7be085a256917e5f(self=0x00007ffeefbfea80, future=<unavailable>) at basic_scheduler.rs:123:8
    frame #15: 0x0000000100002876 dl`tokio::runtime::Runtime::block_on::_$u7b$$u7b$closure$u7d$$u7d$::h3c8c52a8eba3888d at mod.rs:444:33
    frame #16: 0x0000000100002ccb dl`tokio::runtime::context::enter::hbe8cb64eba3a4be5(new=<unavailable>, f=closure-0 @ 0x00007ffeefbfe4f8) at context.rs:72:4
    frame #17: 0x0000000100005272 dl`tokio::runtime::handle::Handle::enter::h88518281dae5787d(self=0x00007ffeefbfeb70, f=closure-0 @ 0x00007ffeefbfe780) at handle.rs:76:8
    frame #18: 0x00000001000027c8 dl`tokio::runtime::Runtime::block_on::h271fc9e842c29956(self=0x00007ffeefbfea78, future=<unavailable>) at mod.rs:441:8
    frame #19: 0x000000010000188b dl`dl::main::h65d62997d02ac27c at main.rs:13:15
    frame #20: 0x00000001000016de dl`std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h8826ec4e8bdf0def at rt.rs:67:33
    frame #21: 0x00000001006a9f08 dl`std::panicking::try::do_call::he58e98df0f664580 [inlined] std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::hab79accac7befc7d at rt.rs:52:12 [opt]
    frame #22: 0x00000001006a9efc dl`std::panicking::try::do_call::he58e98df0f664580 at panicking.rs:303 [opt]
    frame #23: 0x00000001006b126b dl`__rust_maybe_catch_panic at lib.rs:86:7 [opt]
    frame #24: 0x00000001006aa90a dl`std::rt::lang_start_internal::hfa21381d8ff23689 [inlined] std::panicking::try::h1c15a2595ce4d309 at panicking.rs:281:12 [opt]
    frame #25: 0x00000001006aa8d7 dl`std::rt::lang_start_internal::hfa21381d8ff23689 [inlined] std::panic::catch_unwind::h06858616dd2b84f7 at panic.rs:394 [opt]
    frame #26: 0x00000001006aa8d7 dl`std::rt::lang_start_internal::hfa21381d8ff23689 at rt.rs:51 [opt]
    frame #27: 0x00000001000016c1 dl`std::rt::lang_start::h10e8704dbe6771a2(main=(dl`dl::main::h65d62997d02ac27c at main.rs:11), argc=1, argv=0x00007ffeefbff4d0) at rt.rs:67:4
    frame #28: 0x0000000100001d32 dl`main + 34
    frame #29: 0x00007fff7259fcc9 libdyld.dylib`start + 1
dima74

comment created time in 6 hours

issue commentrusoto/rusoto

s3.get_object response body is truncated when using into_blocking_read

I saw something similar.

Even though you likely shouldn't, you cannot use into_blocking_read inside of an async function because it will attempt to start a second Tokio runtime which causes a panic.

It seems like into_blocking_read doesn't really pull it's own weight.

dima74

comment created time in 6 hours

issue commentrust-lang/rfcs

impl PartialEq<Vec<T>> for &'_ [T] / [T; N]

@mbrubeck when I tried similar for Option, it caused a massive amount of fallout due to type inference failures. For example, assert_eq!(foo(), None) is no longer unique and requires a turbofish. I assume something similar would happen here.

sollyucko

comment created time in a day

Pull request review commentshepmaster/snafu

Allow the context selector struct name to be specified

+// This test is the same as basic.rs but with custom context selectors++use snafu::{ResultExt, Snafu};+use std::{+    fs, io,+    path::{Path, PathBuf},+};++#[derive(Debug, Snafu)]+enum Error {+    #[snafu(+        context(OpenConfigContext),

Seeing it in use, I wonder if we want to give some more "namespacing". For example:

        context(name(OpenConfigContext)),

Or

        context(selector(OpenConfigContext)),

Or

        context(selector_name(OpenConfigContext)),
Migi

comment created time in 2 days

Pull request review commentshepmaster/snafu

Allow the context selector struct name to be specified

+// This test is the same as basic.rs but with custom context selectors++use snafu::{ResultExt, Snafu};+use std::{+    fs, io,+    path::{Path, PathBuf},+};++#[derive(Debug, Snafu)]+enum Error {+    #[snafu(+        context(OpenConfigContext),+        display = r#"("Could not open config file at {}: {}", filename.display(), source)"#+    )]+    OpenConfig {+        filename: PathBuf,+        source: io::Error,+    },+    #[snafu(+        context(SaveConfigContext),+        display = r#"("Could not open config file at {}", source)"#

Feel free to use the modern style in these tests:

        display("Could not open config file at {}", source)
Migi

comment created time in 2 days

Pull request review commentshepmaster/snafu

Allow the context selector struct name to be specified

+// This test is the same as basic.rs but with custom context selectors++use snafu::{ResultExt, Snafu};+use std::{+    fs, io,+    path::{Path, PathBuf},+};++#[derive(Debug, Snafu)]+enum Error {+    #[snafu(+        context(OpenConfigContext),+        display = r#"("Could not open config file at {}: {}", filename.display(), source)"#+    )]+    OpenConfig {+        filename: PathBuf,+        source: io::Error,+    },+    #[snafu(+        context(SaveConfigContext),+        display = r#"("Could not open config file at {}", source)"#+    )]+    SaveConfig { source: io::Error },+    #[snafu(+        context(InvalidUserContext),+        display = r#"("User ID {} is invalid", user_id)"#+    )]

This style is fine, which is why it compiles, but I just find it ugly for whatever reason. FWIW, I tend to split it into two:

    #[snafu(context(InvalidUserContext))]
    #[snafu(display = r#"("User ID {} is invalid", user_id)"#)]
Migi

comment created time in 2 days

Pull request review commentshepmaster/snafu

Allow the context selector struct name to be specified

 fn main() { } ``` +## Specifying the generated context selector type's name++Sometimes, you may already have a struct with the same name as one of+the variants of your error enum. You might also have 2 error enums+that share a variant. In those cases, there may be naming collisions+with the context selector type(s) generated by Snafu, which by default

The proper (non-code) name of the library is "SNAFU".

Migi

comment created time in 2 days

Pull request review commentshepmaster/snafu

Allow the context selector struct name to be specified

 fn main() { } ``` +## Specifying the generated context selector type's name++Sometimes, you may already have a struct with the same name as one of+the variants of your error enum. You might also have 2 error enums+that share a variant. In those cases, there may be naming collisions+with the context selector type(s) generated by Snafu, which by default+have the same name as the enum variant.++To solve this, you can specify the name of the context selector that+Snafu should generate, using `#[snafu(context(MySelectorName))]`.++**Example**++```rust+# use snafu::{Snafu, ResultExt};+#+// some struct not related to the error+struct Foo;++#[derive(Debug, Snafu)]+enum Error {+    #[snafu(context(FooContext))]+    Foo {+        source: std::io::Error
        source: std::io::Error,
Migi

comment created time in 2 days

pull request commentshepmaster/snafu

Allow the context selector struct name to be specified

This is also the first step toward #199, where custom context selectors will be needed.

Can you explain a bit more about the general path you see? I don't follow how this plays into that bigger feature.

Migi

comment created time in 2 days

PR opened rust-lang/rust

Correct small typo: 'not' -> 'note'
+2 -2

0 comment

2 changed files

pr created time in 2 days

create barnchshepmaster/rust

branch : typo

created branch time in 2 days

push eventshepmaster/rust

Kevin Per

commit sha dfbc143e65dd4dc8499f7296ddc7889854a8cc7d

Adding if to prevent borrowing suggestion in structs #71136

view details

Kevin Per

commit sha bc29f1d062feeb68207c961d254652d6bff9fd9b

Adding new test #71136

view details

Kevin Per

commit sha d232be80f82b052fd023eb2f4904ccad0aa42d7a

Fix tidy checks

view details

Jeremy Fitzhardinge

commit sha ff9646c0adc149dfff194665785f4daa3aa6d9c3

Stabilize process_set_argv0 feature for Unix This stabilizes process_set_argv0 targeting 1.45.0. It has been useful in practice and seems useful as-is. The equivalent feature could be implemented for Windows, but as far as I know nobody has. That can be done separately. Tracking issue: #66510

view details

Gary Guo

commit sha a23dd0d1e6ddfe6624f1c59e9aefcb59e419610d

Replace fcntl-based file lock with flock WSL1 does not support `fcntl`-based lock and will always report success, therefore creating a race condition when multiple rustc processes are modifying shared data such as `search-index.js`. WSL1 does however support `flock`. `flock` is supported by all unix-like platforms. The only caveat is that Linux 2.6.11 or earlier does not support `flock` on NFS mounts, but as the minimum supported Linux version is 2.6.18, it is not an issue. Fixes #72157

view details

David Cook

commit sha 0148a7f26ce636ac22ac7797dcd7d292c59e8576

InvalidUninitBytes: Track more info about access

view details

Alex Crichton

commit sha cc91041f26fa90920cc96c13c431610153a61586

Always generated object code for `#![no_builtins]` This commit updates the code generation for `#![no_builtins]` to always produce object files instead of conditionally respecting `-Clinker-plugin-lto` and sometimes producing bitcode. This is intended to address rust-lang/cargo#8239. The issue at hand here is that Cargo has tried to get "smarter" about codegen in whole crate graph scenarios. When LTO is enabled it attempts to avoid codegen on as many crates as possible, opting to pass `-Clinker-plugin-lto` where it can to only generate bitcode. When this is combined with `-Zbuild-std`, however, it means that `compiler-builtins` only generates LLVM bitcode instead of object files. Rustc's own LTO passes then explicitly skip `compiler-builtins` (because it wouldn't work anyway) which means that LLVM bitcode gets sent to the linker, which chokes most of the time. The fix in this PR is to not actually respect `-Clinker-plugin-lto` for `#![no_builtins]` crates. These crates, even if slurped up by the linker rather than rustc, will not work with LTO. They define symbols which are only referenced as part of codegen, so LTO's aggressive internalization would trivially remove the symbols only to have the linker realize later that the symbol is undefined. Since pure-bitcode never makes sense for these libraries, the `-Clinker-plugin-lto` flag is silently ignored.

view details

Guillaume Gomez

commit sha 56c494a1487e7fdea07c641e5a182cffba1d15c0

Clean up E0593 explanation

view details

Aaron Hill

commit sha 9b2b8a5afa833795b66267684615497c9547878d

Break tokens before checking if they are 'probably equal' Fixes #68489 When checking two `TokenStreams` to see if they are 'probably equal', we ignore the `IsJoint` information associated with each `TokenTree`. However, the `IsJoint` information determines whether adjacent tokens will be 'glued' (if possible) when construction the `TokenStream` - e.g. `[Gt Gt]` can be 'glued' to `BinOp(Shr)`. Since we are ignoring the `IsJoint` information, 'glued' and 'unglued' tokens are equivalent for determining if two `TokenStreams` are 'probably equal'. Therefore, we need to 'unglue' all tokens in the stream to avoid false negatives (which cause us to throw out the cached tokens, losing span information).

view details

Aaron Hill

commit sha 4a8ccdcc0b518e3c1878ce0be888fd85521b2026

Use a fixed-point iteration when breaking tokens Some tokens need to be broken in a loop until we reach 'unbreakable' tokens.

view details

Gary Guo

commit sha 564ebbb0d19283894e87cd09333375aa0c84f8d9

Use fcntl-based file lock for non-Linux unix

view details

Guillaume Gomez

commit sha e9ae64cca7fd5f88b8c94dbb464594ca13f26792

Improve E0599 explanation

view details

Wesley Wiser

commit sha 8ac1699ea15bf8d6d9bcecec4c340c88cf20c0da

[self-profling] Record the cgu name when doing codegen for a module

view details

Aaron Hill

commit sha 633293fc3a54247f4308507632040424356a9e19

Fix tests

view details

Nathan West

commit sha dc3de7cb2ae9d886ddac91d71f2e9517ff123e90

Add fast-path optimization for Ipv4Addr::fmt

view details

Gary Guo

commit sha a05acbf36f5a1f49919253281fc9bf9465606070

Comment flock usage on Linux

view details

Vadim Petrochenkov

commit sha d0a48d19f5e10869ea4a137d4bb3b84d62966e31

rustllvm: Fix warnings about unused function parameters

view details

Santiago Pastorino

commit sha 93abdd75114dd16e1297370603754fd2ea1e0964

Add some teams to prioritization exclude_labels

view details

David Tolnay

commit sha 6a3aae8aea2448432d4fd0f7c3a22778beaab831

Add flag to open docs: x.py doc --open Tested with: # opens doc/index.html x.py doc --stage 0 --open x.py doc --stage 0 --open src/doc # opens doc/book/index.html x.py doc --stage 0 --open src/doc/book # opens doc/std/index.html x.py doc --stage 0 --open src/libstd # opens doc/proc_macro/index.html x.py doc --stage 0 --open src/libproc_macro # opens both x.py doc --stage 0 --open src/libstd src/libproc_macro

view details

Kevin Per

commit sha e776121431dc73b6e1782d5ddcc4e8d6d714f8e4

Using `!span.from_expansion()` instead of snippets

view details

push time in 2 days

pull request commentrust-lang/rfcs

Add clamp RFC

All accepted RFCs are visible on the website:

https://rust-lang.github.io/rfcs/1961-clamp.html

find out what clamp even is

https://doc.rust-lang.org/std/primitive.f32.html#method.clamp

Restrict a value to a certain interval unless it is NaN.

Returns max if self is greater than max, and min if self is less than min. Otherwise this returns self.

Not that this function returns NaN if the initial value was NaN as well.

Xaeroxe

comment created time in 2 days

pull request commentrust-lang/rust

Fix asinh of negative values

I don't have the numerical chops to evaluate this, so I'll be reassigning, but it seems like a really bad idea to "fix" this without (a) even stating what the problem is in the commit message (links to external sites are great, but ultimately very fragile) (b) adding any kind of automated test to prevent regressions.

r? @cramertj

Ralith

comment created time in 2 days

pull request commentrust-lang/rust

add warning sign to UB examples

@bors r+ rollup

RalfJung

comment created time in 2 days

pull request commentrust-lang/rust

add warning sign to UB examples

I don't know how to make it yellow

⚠️

U+26A0 : WARNING SIGN
U+FE0F : VARIATION SELECTOR-16 [VS16] {emoji variation selector}

⚠︎

U+26A0 : WARNING SIGN
U+FE0E : VARIATION SELECTOR-15 [VS15] {text variation selector}
RalfJung

comment created time in 2 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 pub struct Command {     gid: Option<gid_t>,     saw_nul: bool,     closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,+    groups: Option<Vec<gid_t>>,

if setgroups(0, NULL) is functionally equivalent to setgroups(0, groups)

FWIW, I was arguing for the equivalent of

if self.groups.is_empty() {
    set_them()
}

instead of

if let Some(groups) = self.groups {
    set_them()
}

Not that you should unconditionally call the C API. Maybe you need to unconditionally call it (I don't know this API), but if so, you could do

let (ptr, count) = if self.groups.is_empty() {
    (nullptr, 0)
} else {
    (groups.as_ptr, groups.len())
};
slo1

comment created time in 2 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 impl Command {     pub fn get_gid(&self) -> Option<gid_t> {         self.gid     }+    #[allow(dead_code)]+    pub fn get_groups(&self) -> &Option<Vec<gid_t>> {

it can be Option<Box<[gid_t]>>

It cannot as this is &self and we can't move out of a reference. Here are the possible ways I see it:

type gid_t = u8;

struct Example {
    groups_option: Option<Vec<gid_t>>,
    groups_vec: Vec<gid_t>,
    groups_slice: Box<[gid_t]>,
}

impl Example {
    fn option_bare(&self) -> &[gid_t] {
        self.groups_option.as_deref().unwrap_or_default()
    }

    fn option_option(&self) -> Option<&[gid_t]> {
        self.groups_option.as_deref()
    }

    fn vec_bare(&self) -> &[gid_t] {
        &self.groups_vec
    }

    fn vec_option(&self) -> Option<&[gid_t]> {
        if self.groups_vec.is_empty() {
            None
        } else {
            Some(&self.groups_vec)
        }
    }

    fn slice_bare(&self) -> &[gid_t] {
        &self.groups_slice
    }

    fn slice_option(&self) -> Option<&[gid_t]> {
        if self.groups_slice.is_empty() {
            None
        } else {
            Some(&self.groups_slice)
        }
    }
}
slo1

comment created time in 2 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 pub struct Command {     gid: Option<gid_t>,     saw_nul: bool,     closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,+    groups: Option<Vec<gid_t>>,

If so, then it seems that we need an "unset groups" method to perform the equivalent of self.groups = None.

slo1

comment created time in 2 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 pub struct Command {     gid: Option<gid_t>,     saw_nul: bool,     closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,+    groups: Option<Vec<gid_t>>,

So there’s a difference between calling .groups(vec![]) and never calling it to start with?

slo1

comment created time in 2 days

pull request commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

As this appears to add public API (perhaps insta-stable), I'm gonna tag in...

r? @JoshTriplett

slo1

comment created time in 3 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 impl Command {     pub fn get_gid(&self) -> Option<gid_t> {         self.gid     }+    #[allow(dead_code)]+    pub fn get_groups(&self) -> &Option<Vec<gid_t>> {

&Option<Vec<gid_t>> is not a useful type. It probably should be Option<&[gid_t]> for ease-of use. If we change to a Vec<_>, it could be &[gid_t].

slo1

comment created time in 3 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 pub trait CommandExt {     #[stable(feature = "rust1", since = "1.0.0")]     fn gid(&mut self, id: u32) -> &mut process::Command; +    /// Sets the supplementary group IDs for the calling process. Translates to+    /// a `setgroups` call in the child process.+    #[stable(feature = "rust1", since = "1.0.0")]

This cannot have existed since Rust 1.0...

slo1

comment created time in 3 days

Pull request review commentrust-lang/rust

Add setgroups to std::os::unix::process::CommandExt

 pub struct Command {     gid: Option<gid_t>,     saw_nul: bool,     closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,+    groups: Option<Vec<gid_t>>,

Option<Vec<_>> is always a little suspicious; do we need to differentiate between empty and non-existent?

slo1

comment created time in 3 days

pull request commentrust-lang/rust

add warning sign to UB examples

r=me if my questions were what you intended.

RalfJung

comment created time in 3 days

Pull request review commentrust-lang/rust

add warning sign to UB examples

 impl<T> MaybeUninit<T> {     /// x.write(Some(vec![0,1,2]));     /// let x1 = unsafe { x.read() };     /// let x2 = unsafe { x.read() };-    /// // We now created two copies of the same vector, leading to a double-free when+    /// // We now created two copies of the same vector, leading to a double-free ⚠️ when

Just to be clear, this ⚠︎ is supposed to be in the middle of the sentence, right?

RalfJung

comment created time in 3 days

push eventshepmaster/rust

Camille GILLOT

commit sha 49e024ee7c404fb767d8560b1cf248929f8d7574

Monomorphise the interface.

view details

Camille GILLOT

commit sha 282d72f6bb6ad8c00b9d5bbd06e7946a37d412ef

Inline a few things.

view details

Camille GILLOT

commit sha e4976d0caf6a4b9d9d3ec8979427c6e744c8c38d

Restrict access.

view details

Oliver Scherer

commit sha 004208fc46467c5e171e739559f77c0fafbbe87a

Move recursion check for zsts back to read site instead of access check site.

view details

LeSeulArtichaut

commit sha f2c6cbd98fa8be80951385f789f49d560916c726

Prevent calls to functions with `#[target_feature]` in safe contexts

view details

LeSeulArtichaut

commit sha f9b9ba51d3a5d3e5ae3db4a351c2be47e36ef366

Prevent functions with `#[target_feature]` to be coerced to safe function pointers

view details

Tshepang Lekhonkhobe

commit sha 7a1a69911b96555396ffafe7d28a9f59912db72e

make Stability docs a bit more readable, and fix some rustdoc warnings

view details

Nicholas Nethercote

commit sha d4e5e1bcffe7feb150d061985eb03c6e09ebb9f7

Don't copy bytecode files into the incr. comp. cache. It's no longer necessary now that bitcode is embedded into object files. This change meant that `WorkProductFileKind::Bytecode` is no longer necessary, which means that type is no longer necessary, which allowed several places in the code to become simpler.

view details

LeSeulArtichaut

commit sha 8d9f73a6902064e1a5508af98d77aed2ae055944

Add new tests and bless old tests

view details

Oliver Scherer

commit sha ba26df099846165de7bb24486cced1cdfb40132b

Name test appropriately and link to the issues it regress-checks for

view details

Camille GILLOT

commit sha 6cde87def1b9de2c7746523d7f19fe3a76d1893d

Mark query function as must_use.

view details

Oliver Scherer

commit sha c64c7766787e6d9d5b80b95832155a11cadcbe40

Remove a comment that made only sense in the original position of this cycle check.

view details

Tshepang Lekhonkhobe

commit sha 5f4b7a4585bd011a5b889f74170b208508b48c64

fix doc reference Should of been in e0ce9f8c0a97e5949c9cadd220279d6969289daa, but that had a typo.

view details

Alex Crichton

commit sha e1832fa4e4700a5f8935ac3e88f7688a15e17df0

Rename `bitcode-in-rlib` option to `embed-bitcode` This commit finishes work first pioneered in #70458 and started in #71528. The `-C bitcode-in-rlib` option, which has not yet reached stable, is renamed to `-C embed-bitcode` since that more accurately reflects what it does now anyway. Various tests and such are updated along the way as well. This'll also need to be backported to the beta channel to ensure we don't accidentally stabilize `-Cbitcode-in-rlib` as well.

view details

bors

commit sha d2708873ef711ec8ab45df1e984ecf24a96cd369

Auto merge of #5548 - matthiaskrgr:traget_os, r=flip1995 mismatched_target_os: link to respective section in rust reference changelog: none

view details

Dylan DPC

commit sha 54013532cd5db91219127eed2f9f1e7045154ecb

Rollup merge of #71744 - tshepang:obsolete-comment, r=Dylan-DPC remove obsolete comment Referenced was removed in 8770d0f34a9bee2691517ab95803a277c453dfda

view details

Dylan DPC

commit sha d83d8be65d92f00f816f5ae55731c6416b2ff849

Rollup merge of #71747 - spastorino:safety-scheme-around-consts-cleanup, r=oli-obk Remove deadcode in eval_mir_constant_to_operand r? @oli-obk @RalfJung

view details

Dylan DPC

commit sha 8d9912ef49ceed0b813d7b75344fe9e261044f93

Rollup merge of #71749 - RalfJung:miri-error-print, r=oli-obk fix Miri error message padding I screwed up in the previous PR, and accidentally padded with spaces instead of 0s... r? @oli-obk

view details

Dylan DPC

commit sha 2a3c2b335bb119ed34ece69edc837778266c821d

Rollup merge of #71752 - tshepang:more-readable, r=jonas-schievink make Stability doc a more readable (and fix rustdoc warning)

view details

Dylan DPC

commit sha da42f6897e30e182b3372b996de2be3da3f42ed5

Rollup merge of #71755 - tshepang:fix-comment, r=jonas-schievink fix doc reference Should of been in e0ce9f8c0a97e5949c9cadd220279d6969289daa, but that had a typo.

view details

push time in 3 days

issue closeddeniszholob/factorio-cheat-sheet

Sprites randomly permute when scrolling on iOS Safari

https://imgur.com/a/yL0OlEq

Seems related to #66

Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Mobile/15E148 Safari/604.1

closed time in 3 days

shepmaster

issue commentdeniszholob/factorio-cheat-sheet

Sprites randomly permute when scrolling on iOS Safari

I have not used the site in 6 months or so. A cursory check now doesn't immediately show the problem.

shepmaster

comment created time in 3 days

issue commentshepmaster/snafu

Snafu for enums with variant types

A recent comment on Discord seems like it would be helped by this:

[Is there] an easy way to hide the types used in the enum struct variants. I don't really want a fully opaque type, but I don't want to expose the dependency types I'm using for the error messages in the API either.

Instead of

enum Error {
    Example { source: ApiError },
}

it could be

enum Error {
    Example(ExampleOpaque),
}

struct ExampleOpaque { source: ApiError }
Migi

comment created time in 3 days

pull request commentrust-lang/rust

split_inclusive: add tracking issue number (72360)

@bors r+ rollup

golddranks

comment created time in 6 days

delete branch shepmaster/fuzzy-pickles

delete branch : associated-type-docs

delete time in 6 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 339c45071d4e3c84e95a3ac752062ea3d4ff76aa

Add documentation to associated type equals and bounds structs

view details

Jake Goulding

commit sha 1bac78c992a5db11026f76c902dd3934bc6b32b8

Merge pull request #134 from shepmaster/associated-type-docs

view details

push time in 6 days

create barnchshepmaster/fuzzy-pickles

branch : associated-type-docs

created branch time in 7 days

delete branch shepmaster/fuzzy-pickles

delete branch : generic-const-decls

delete time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 435739538474bee809d66df07a1a4e43f2779de5

Support generic constant declarations

view details

Jake Goulding

commit sha 42aafe4c43c76a7fe2369dea4baeb466371c7043

Merge pull request #133 from shepmaster/generic-const-decls

view details

push time in 7 days

PR opened shepmaster/fuzzy-pickles

Support generic constant declarations enhancement
+49 -0

0 comment

3 changed files

pr created time in 7 days

create barnchshepmaster/fuzzy-pickles

branch : generic-const-decls

created branch time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 88b53eb849270574d695bd2d5564d1ba54575d1f

Update subslice patterns to require the at-sign

view details

Jake Goulding

commit sha 2b1b2d845408b06f5f7801dcd67ca6564df2b203

Merge pull request #132 from shepmaster/update-subslice-syntax

view details

push time in 7 days

delete branch shepmaster/fuzzy-pickles

delete branch : update-subslice-syntax

delete time in 7 days

create barnchshepmaster/fuzzy-pickles

branch : update-subslice-syntax

created branch time in 7 days

delete branch shepmaster/fuzzy-pickles

delete branch : associated-type-trait-bounds

delete time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 2824424c90c9d68ab5c3f0b62c319b3a7c179f3b

Trait bounds on associated types

view details

Jake Goulding

commit sha 3ebdbe4a963edbc64df02e958cc05a607e76418e

Merge pull request #131 from shepmaster/associated-type-trait-bounds

view details

push time in 7 days

Pull request review commentrust-lang/rfcs

RFC: Reading into uninitialized buffers

+- Feature Name: read_buf+- Start Date: 2020/05/18+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Summary+[summary]: #summary++The current design of the `Read` trait is nonoptimal as it requires that the buffer passed to its various methods be+pre-initialized even though the contents will be immediately overwritten. This RFC proposes an interface to allow+implementors and consumers of `Read` types to robustly and soundly work with uninitialized buffers.++# Motivation+[motivation]: #motivation++## Background+[motivation-background]: #motivation-background++The core of the `Read` trait looks like this:++```rust+pub trait Read {+    /// Reads data into `buf`, returning the number of bytes written.+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;+}+```++Code working with a reader needs to create the buffer that will be passed to read; the simple approach is something like+this:++```rust+let mut buf = [0; 1024];+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, that approach isn't ideal since the work spent to zero the buffer is wasted. The reader should be overwriting+the part of the buffer we're working with, after all. Ideally, we wouldn't have to perform any initialization at all:++```rust+let mut buf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() };+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, this is unsound when working with an arbitrary reader. The `Read` trait is not unsafe, so the soundness of+working with an implementation can't depend on the "reasonableness" of the implementation for soundness. The+implementation could read from the buffer, or return the wrong number of bytes read:++```rust+struct BrokenReader;++impl Read for BrokenReader {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        Ok(buf.len())+    }+}++struct BrokenReader2;++impl Read for BrokenReader2 {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        if buf[0] == 0 {+            buf[0] = 1;+        } else {+            buf[0] = 2;+        }++        Ok(1)+    }+}+```++In either case, the `process_data` call above would be working with uninitialized memory. Uninitialized memory is a+dangerous (and often misunderstood) beast. Uninitialized memory does not have an *arbitrary* value; it actually has an+*undefined* value. Undefined values can very quickly turn into undefined behavior. Check out+[Ralf's blog post](https://www.ralfj.de/blog/2019/07/14/uninit.html) for a more extensive discussion of uninitialized+memory.++## But how bad are undefined values really?+[motivation-badness]: #motivation-badness++Are undefined values *really* that bad in practice? Consider a function that tries to use an uninitialized buffer with+a reader:++```rust+fn unsound_read_u32_be<R>(r: &mut R) -> io::Result<u32>+where+    R: Read,+{+    let mut buf: [u8; 4] = unsafe { MaybeUninit::uninit().assume_init() };+    r.read_exact(&mut buf)?;+    Ok(u32::from_be_bytes(buf))+}+```++Now consider this function that tries to use `unsound_read_u32_be`:++```rust+pub fn blammo() -> NonZeroU32 {+    let n = unsound_read_u32_be(&mut BrokenReader).unwrap();+    NonZeroU32::new(n).unwrap_or(NonZeroU32::new(1).unwrap())+}+```++It should clearly only be able to return a nonzero value, but if we compile it using rustc 1.42.0 for the+x86_64-unknown-linux-gnu target, the function [compiles down to this](https://rust.godbolt.org/z/Y9rL-5):++```asm+example::blammo:+        ret+```++That means that it will return whatever arbitrary number happened to be in the `%rax` register. That could very well+happen to be 0, which violates the invariant of `NonZeroU32` and any upstream callers of `blammo` will have a bad time.+Because the value that `unsound_read_u32_be` returned was undefined, the compiler completely removed the check for 0!++We want to be able to take advantage of the improved performance of avoiding buffer initialization without triggering+undefined behavior in safe code.++## Why not just initialize?+[motivation-why]: #motivation-why++If working with uninitialized buffers carries these risks, why should we bother with it at all? Code dealing with IO in+both the standard library and the ecosystem today already works with uninitialized buffers because there are concrete,+nontrivial performance improvements from doing so:++* [The standard library measured](https://github.com/rust-lang/rust/pull/26950) a 7% improvement in benchmarks all the+    way back in 2015.+* [The hyper HTTP library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-554543881) a nontrivial+    improvement in benchmarks.+* [The Quinn QUIC library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553501198) a 0.2%-2.45%+    improvement in benchmarks.++Given that the ecosystem has already found that uninitialized buffer use is important enough to deal with, the standard+library should provide a more robust framework to work with.++In addition, working with regular initialized buffers can be *more complex* than working with uninitialized buffers!+Back in 2015, the standard library's implementation of `Read::read_to_end` was found to be wildly inefficient due to+insufficiently careful management of buffer sizes because it was initializing them.+[The fix](https://github.com/rust-lang/rust/pull/23820) improved the performance of small reads by over 4,000x! If+the buffer did not need to be initialized, the simpler implementation would have been fine.++# Guide-level explanation+[guide-level-explanation]: #guide-level-explanation++The `ReadBuf` type manages a *progressively initialized* buffer of bytes. It is primarily used to avoid buffer+initialization overhead when working with types implementing the `Read` trait. It wraps a buffer of+possibly-uninitialized bytes and tracks how much of the buffer has been initialized and how much of the buffer has been+written to. Tracking the set of initialized bytes allows initialization costs to only be paid once, even if the buffer+is used repeatedly in a loop.++Here's a small example of working with a reader using a `ReadBuf`:++```rust+// The base level buffer uses the `MaybeUninit` type to avoid having to initialize the whole 8kb of memory up-front.+let mut buf = [MaybeUninit::<u8>::uninit(); 8192];++// We then wrap that in a `ReadBuf` to track the state of the buffer.+let mut buf = ReadBuf::uninit(&mut buf);++loop {+    // Read some data into the buffer.+    some_reader.read_buf(&mut buf)?;++    // If nothing was written into the buffer, we're at EOF.+    if buf.written().is_empty() {+        break;+    }++    // Otherwise, process the data.+    process_data(buf.written());++    // And then clear the buffer out so we can read into it again. This just resets the amount of data "written" to 0,+    // but preserves the memory of how much of the buffer has been initialized.+    buf.clear();+}+```++It is important that we created the `ReadBuf` outside of the loop. If we instead created it in each loop iteration we+would fail to preserve the knowledge of how much of it has been initialized.++When implementing `Read`, the author can choose between an entirely safe interface that exposes an initialized buffer,+or an unsafe interface that allows the code to work directly with the uninitialized buffer for higher performance.++A safe `Read` implementation:++```rust+impl Read for MyReader {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        // Get access to the unwritten part of the buffer, making sure it has been fully initialized. Since `ReadBuf`+        // tracks the initialization state of the buffer, this is "free" after the first time it's called.+        let unwritten: &mut [u8] = buf.initialize_unwritten();++        // Fill the whole buffer with some nonsense.+        for (i, byte) in unwritten.iter_mut().enumerate() {+            *byte = i as u8;+        }++        // And indicate that we've written the whole thing.+        let len = unwritten.len();+        buf.add_written(len);++        Ok(())+    }+}+```++An unsafe `Read` implementation:++```rust+impl Read for TcpStream {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        unsafe {+            // Get access to the unwritten part of the buffer, without initializing it. This method is unsafe; we are+            // responsible for ensuing that we don't "de-initialize" portions of it that have previously been+            // initialized.+            let unwritten: &mut [MaybeUninit<u8>] = buf.unwritten_mut();++            // We're just delegating to the libc read function, which returns an `isize`. The return value indicates+            // an error if negative and the number of bytes read otherwise.+            let nread = libc::read(self.fd, unwritten.as_mut_ptr().cast::<libc::c_void>(), unwritten.len());++            if nread < 0 {+                return Err(io::Error::last_os_error());+            }++            let nread = nread as usize;+            // If the read succeeded, tell the buffer that the read-to portion has been initialized. This method is+            // unsafe; we are responsible for ensuring that this portion of the buffer has actually been initialized.+            buf.assume_initialized(nread);+            // And indicate that we've written the bytes as well. Unlike `assume_initialized`, this method is safe,+            // and asserts that the written portion of the buffer does not advance beyond the initialized portion of+            // the buffer. If we didn't call `assume_initialized` above, this call could panic.+            buf.add_written(nread);++            Ok(())+        }+    }+}+```++# Reference-level explanation+[reference-level-explanation]: #reference-level-explanation++```rust+/// A wrapper around a byte buffer that is incrementally written to and initialized.+///+/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the+/// buffer that has been logically written to, a region that has been initialized at some point but not yet logically+/// written to, and a region at the end that is fully uninitialized. The written-to region is guaranteed to be a+/// subset of the initialized region.+pub struct ReadBuf<'a> {+    buf: &'a mut [MaybeUninit<u8>],+    written: usize,+    initialized: usize,+}

That's covered briefly in the future possibilities section.

Are you referring to this section?

There is probably some kind of abstraction that could be defined to encapsulate that logic.

If so, perhaps that section could be extended a bit; I've re-read it a few times and still can't see a connection to an owning version.

sfackler

comment created time in 7 days

create barnchshepmaster/fuzzy-pickles

branch : associated-type-trait-bounds

created branch time in 7 days

Pull request review commentrust-lang/rfcs

RFC: Reading into uninitialized buffers

+- Feature Name: read_buf+- Start Date: 2020/05/18+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Summary+[summary]: #summary++The current design of the `Read` trait is nonoptimal as it requires that the buffer passed to its various methods be+pre-initialized even though the contents will be immediately overwritten. This RFC proposes an interface to allow+implementors and consumers of `Read` types to robustly and soundly work with uninitialized buffers.++# Motivation+[motivation]: #motivation++## Background+[motivation-background]: #motivation-background++The core of the `Read` trait looks like this:++```rust+pub trait Read {+    /// Reads data into `buf`, returning the number of bytes written.+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;+}+```++Code working with a reader needs to create the buffer that will be passed to read; the simple approach is something like+this:++```rust+let mut buf = [0; 1024];+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, that approach isn't ideal since the work spent to zero the buffer is wasted. The reader should be overwriting+the part of the buffer we're working with, after all. Ideally, we wouldn't have to perform any initialization at all:++```rust+let mut buf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() };+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, this is unsound when working with an arbitrary reader. The `Read` trait is not unsafe, so the soundness of+working with an implementation can't depend on the "reasonableness" of the implementation for soundness. The+implementation could read from the buffer, or return the wrong number of bytes read:++```rust+struct BrokenReader;++impl Read for BrokenReader {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        Ok(buf.len())+    }+}++struct BrokenReader2;++impl Read for BrokenReader2 {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        if buf[0] == 0 {+            buf[0] = 1;+        } else {+            buf[0] = 2;+        }++        Ok(1)+    }+}+```++In either case, the `process_data` call above would be working with uninitialized memory. Uninitialized memory is a+dangerous (and often misunderstood) beast. Uninitialized memory does not have an *arbitrary* value; it actually has an+*undefined* value. Undefined values can very quickly turn into undefined behavior. Check out+[Ralf's blog post](https://www.ralfj.de/blog/2019/07/14/uninit.html) for a more extensive discussion of uninitialized+memory.++## But how bad are undefined values really?+[motivation-badness]: #motivation-badness++Are undefined values *really* that bad in practice? Consider a function that tries to use an uninitialized buffer with+a reader:++```rust+fn unsound_read_u32_be<R>(r: &mut R) -> io::Result<u32>+where+    R: Read,+{+    let mut buf: [u8; 4] = unsafe { MaybeUninit::uninit().assume_init() };+    r.read_exact(&mut buf)?;+    Ok(u32::from_be_bytes(buf))+}+```++Now consider this function that tries to use `unsound_read_u32_be`:++```rust+pub fn blammo() -> NonZeroU32 {+    let n = unsound_read_u32_be(&mut BrokenReader).unwrap();+    NonZeroU32::new(n).unwrap_or(NonZeroU32::new(1).unwrap())+}+```++It should clearly only be able to return a nonzero value, but if we compile it using rustc 1.42.0 for the+x86_64-unknown-linux-gnu target, the function [compiles down to this](https://rust.godbolt.org/z/Y9rL-5):++```asm+example::blammo:+        ret+```++That means that it will return whatever arbitrary number happened to be in the `%rax` register. That could very well+happen to be 0, which violates the invariant of `NonZeroU32` and any upstream callers of `blammo` will have a bad time.+Because the value that `unsound_read_u32_be` returned was undefined, the compiler completely removed the check for 0!++We want to be able to take advantage of the improved performance of avoiding buffer initialization without triggering+undefined behavior in safe code.++## Why not just initialize?+[motivation-why]: #motivation-why++If working with uninitialized buffers carries these risks, why should we bother with it at all? Code dealing with IO in+both the standard library and the ecosystem today already works with uninitialized buffers because there are concrete,+nontrivial performance improvements from doing so:++* [The standard library measured](https://github.com/rust-lang/rust/pull/26950) a 7% improvement in benchmarks all the+    way back in 2015.+* [The hyper HTTP library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-554543881) a nontrivial+    improvement in benchmarks.+* [The Quinn QUIC library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553501198) a 0.2%-2.45%+    improvement in benchmarks.++Given that the ecosystem has already found that uninitialized buffer use is important enough to deal with, the standard+library should provide a more robust framework to work with.++In addition, working with regular initialized buffers can be *more complex* than working with uninitialized buffers!+Back in 2015, the standard library's implementation of `Read::read_to_end` was found to be wildly inefficient due to+insufficiently careful management of buffer sizes because it was initializing them.+[The fix](https://github.com/rust-lang/rust/pull/23820) improved the performance of small reads by over 4,000x! If+the buffer did not need to be initialized, the simpler implementation would have been fine.++# Guide-level explanation+[guide-level-explanation]: #guide-level-explanation++The `ReadBuf` type manages a *progressively initialized* buffer of bytes. It is primarily used to avoid buffer+initialization overhead when working with types implementing the `Read` trait. It wraps a buffer of+possibly-uninitialized bytes and tracks how much of the buffer has been initialized and how much of the buffer has been+written to. Tracking the set of initialized bytes allows initialization costs to only be paid once, even if the buffer+is used repeatedly in a loop.++Here's a small example of working with a reader using a `ReadBuf`:++```rust+// The base level buffer uses the `MaybeUninit` type to avoid having to initialize the whole 8kb of memory up-front.+let mut buf = [MaybeUninit::<u8>::uninit(); 8192];++// We then wrap that in a `ReadBuf` to track the state of the buffer.+let mut buf = ReadBuf::uninit(&mut buf);++loop {+    // Read some data into the buffer.+    some_reader.read_buf(&mut buf)?;++    // If nothing was written into the buffer, we're at EOF.+    if buf.written().is_empty() {+        break;+    }++    // Otherwise, process the data.+    process_data(buf.written());++    // And then clear the buffer out so we can read into it again. This just resets the amount of data "written" to 0,+    // but preserves the memory of how much of the buffer has been initialized.+    buf.clear();+}+```++It is important that we created the `ReadBuf` outside of the loop. If we instead created it in each loop iteration we+would fail to preserve the knowledge of how much of it has been initialized.++When implementing `Read`, the author can choose between an entirely safe interface that exposes an initialized buffer,+or an unsafe interface that allows the code to work directly with the uninitialized buffer for higher performance.++A safe `Read` implementation:++```rust+impl Read for MyReader {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        // Get access to the unwritten part of the buffer, making sure it has been fully initialized. Since `ReadBuf`+        // tracks the initialization state of the buffer, this is "free" after the first time it's called.+        let unwritten: &mut [u8] = buf.initialize_unwritten();++        // Fill the whole buffer with some nonsense.+        for (i, byte) in unwritten.iter_mut().enumerate() {+            *byte = i as u8;+        }++        // And indicate that we've written the whole thing.+        let len = unwritten.len();+        buf.add_written(len);++        Ok(())+    }+}+```++An unsafe `Read` implementation:++```rust+impl Read for TcpStream {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        unsafe {+            // Get access to the unwritten part of the buffer, without initializing it. This method is unsafe; we are+            // responsible for ensuing that we don't "de-initialize" portions of it that have previously been+            // initialized.+            let unwritten: &mut [MaybeUninit<u8>] = buf.unwritten_mut();++            // We're just delegating to the libc read function, which returns an `isize`. The return value indicates+            // an error if negative and the number of bytes read otherwise.+            let nread = libc::read(self.fd, unwritten.as_mut_ptr().cast::<libc::c_void>(), unwritten.len());++            if nread < 0 {+                return Err(io::Error::last_os_error());+            }++            let nread = nread as usize;+            // If the read succeeded, tell the buffer that the read-to portion has been initialized. This method is+            // unsafe; we are responsible for ensuring that this portion of the buffer has actually been initialized.+            buf.assume_initialized(nread);+            // And indicate that we've written the bytes as well. Unlike `assume_initialized`, this method is safe,+            // and asserts that the written portion of the buffer does not advance beyond the initialized portion of+            // the buffer. If we didn't call `assume_initialized` above, this call could panic.+            buf.add_written(nread);++            Ok(())+        }+    }+}+```++# Reference-level explanation+[reference-level-explanation]: #reference-level-explanation++```rust+/// A wrapper around a byte buffer that is incrementally written to and initialized.+///+/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the+/// buffer that has been logically written to, a region that has been initialized at some point but not yet logically+/// written to, and a region at the end that is fully uninitialized. The written-to region is guaranteed to be a+/// subset of the initialized region.+pub struct ReadBuf<'a> {+    buf: &'a mut [MaybeUninit<u8>],+    written: usize,+    initialized: usize,+}++impl<'a> ReadBuf<'a> {+    /// Creates a new `ReadBuf` from a fully initialized buffer.+    #[inline]+    pub fn new(buf: &'a mut [u8]) -> ReadBuf<'a> { ... }++    /// Creates a new `ReadBuf` from a fully uninitialized buffer.+    ///+    /// Use `assume_initialized` if part of the buffer is known to be already inintialized.+    #[inline]+    pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> { ... }++    /// Returns the full size of the buffer.+    #[inline]+    pub fn len(&self) -> usize { ... }++    /// Returns a shared reference to the written-to portion of the buffer.+    #[inline]+    pub fn written(&self) -> &[u8] { ... }++    /// Returns a mutable reference to the written-to portion of the buffer.+    #[inline]+    pub fn written_mut(&mut self) -> &mut [u8] { ... }++    /// Returns a shared reference to the initialized portion of the buffer.+    ///+    /// This includes the written-to portion.+    #[inline]+    pub fn initialized(&self) -> &[u8] { ... }++    /// Returns a mutable reference to the initialized portion of the buffer.+    ///+    /// This includes the written-to portion.+    #[inline]+    pub fn initialized_mut(&mut self) -> &mut [u8] { ... }++    /// Returns a mutable reference to the unwritten-to part of the buffer without ensuring that it has been fully+    /// initialized.+    ///+    /// # Safety+    ///+    /// The caller must not de-initialize portions of the buffer that have already been initialized.+    #[inline]+    pub unsafe fn unwritten_mut(&mut self) -> &mut [MaybeUninit<u8>] { ... }++    /// Returns a mutable reference to the unwritten-to part of the buffer, ensuring it is fully initialized.+    ///+    /// Since `ReadBuf` tracks the region of the buffer that has been initialized, this is effectively "free" after+    /// the first use.+    #[inline]+    pub fn initialize_unwritten(&mut self) -> &mut [u8] { ... }++    /// Returns a mutable reference to the first `n` bytes of the unwritten-to part of the buffer, ensuring it is+    /// fully initialized.+    ///+    /// # Panics+    ///+    /// Panics if `self.remaining()` is less than `n`.+    #[inline]+    pub fn initialize_unwriten_to(&mut self, n: usize) -> &mut [u8] { ... }++    /// Returns the number of bytes at the end of the slice that have not yet been written to.+    #[inline]+    pub fn remaining(&self) -> usize { ... }++    /// Clears the buffer, resetting the written-to region to empty.+    ///+    /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.+    #[inline]+    pub fn clear(&mut self) { ... }++    /// Increases the size of the written-to region of the buffer.+    ///+    /// The number of initialized bytes is not changed.+    ///+    /// # Panics+    ///+    /// Panics if the written region of the buffer would become larger than the initialized region.+    #[inline]+    pub fn add_written(&mut self, n: usize) { ... }++    /// Sets the size of the written-to region of the buffer.+    ///+    /// The number of initialized bytes is not changed.+    ///+    /// Note that this can be used to *shrink* the written region of the buffer in addition to growing it (for+    /// example, by a `Read` implementation that compresses data in-place).+    ///+    /// # Panics+    ///+    /// Panics if the written region of the buffer would become larger than the intialized region.+    #[inline]+    pub fn set_written(&mut self, n: usize) { ... }++    /// Asserts that the first `n` unwritten bytes of the buffer are initialized.+    ///+    /// `ReadBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer+    /// bytes than are already known to be initialized.+    ///+    /// # Safety+    ///+    /// The caller must ensure that `n` unwritten bytes of the buffer have already been initialized.+    #[inline]+    pub unsafe fn assume_initialized(&mut self, n: usize) { ... }++    /// Appends data to the buffer, advancing the written position and possibly also the initialized position.+    ///+    /// # Panics+    ///+    /// Panics if `self.remaining()` is less than `buf.len()`.+    #[inline]+    pub fn append(&mut self, buf: &[u8]) { ... }+}+```++The `Read` trait uses this type in some of its methods:++```rust+pub trait Read {+    /// The existing `read` method gains a default implementation that delegates to `read_buf`.+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        let mut buf = ReadBuf::new(buf);+        self.read_buf(&mut buf)?;+        Ok(buf.written().len())+    }++    /// Pull some bytes from this source into the specified buffer.+    ///+    /// This is equivalent to the `read` method, except that it is passed a `ReadBuf` rather than `[u8]` to allow use+    /// with uninitialized buffers.+    ///+    /// The default implementation delegates to `read`.+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        let n = self.read(buf.initialize_unwritten())?;+        buf.add_written(n);+        Ok(())+    }++    ...+}+```++The `ReadBuf` type wraps a buffer of maybe-initialized bytes and tracks how much of the buffer has already been+initialized. This tracking is crucial because it avoids repeated initialization of already-initialized portions of the+buffer. It additionally provides the guarantee that the initialized portion of the buffer *is actually initialized*! A+subtle characteristic of `MaybeUninit` is that you can de-initialize values in addition to initializing them, and this+API protects against that.++It additionally tracks the amount of data read into the buffer directly so that code working with `Read` implementations+can be guaranteed that the region of the buffer that the reader claims was written to is minimally initialized.+Thinking back to the `BrokenReader` in the motivation section, the worst an implementation can now do (without writing+unsound unsafe code) is to fail to actually write useful data into the buffer. Code using a `BrokenReader` may see bad+data in the buffer, but the bad data at least has defined contents now!++Note that `read` and `read_buf` have default implementations that delegate to each other. `read_buf` must have a default+implementation for backwards compatibility, and we want `read` to have a default implementation so that `Read`+implementations can simply contain code for `read_buf`. This cycle does mean that a trivial `Read` implementation of+`impl Read for Foo {}` will compile but calls will infinitely recurse. We would ideally create a lint that exactly one+of the two methods is implemented, but that is not a hard requirement. Such an implementation is pretty obviously+ridiculous and this hopefully shouldn't be a huge problem in practice.++Some of `Read`'s convenience methods will be modified to take advantage of `read_buf`, and some new convenience methods+will be added:++```rust+pub trait Read {+    /// Read the exact number of bytes required to fill `buf`.+    ///+    /// This is equivalent to the `read_exact` method, except that it is passed a `ReadBuf` rather than `[u8]` to+    /// allow use with uninitialized buffers.+    fn read_buf_exact(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        while buf.remaining() > 0 {+            let prev_written = buf.written().len();+            match self.read_buf(&mut buf) {+                Ok(()) => {}+                Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,+                Err(e) => return Err(e),+            }++            if buf.written.len() == prev_written {+                return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill buffer"));+            }+        }++        Ok(())+    }++    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {+        let initial_len = buf.len();++        let mut initialized = 0;+        loop {+            if buf.len() == buf.capacity() {+                buf.reserve(32);+            }++            let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut());+            unsafe {+                read_buf.assume_initialized(initialized);+            }++            match self.read_buf(&mut read_buf) {+                Ok(()) => {}+                Err(e) if e.kind() = io::ErrorKind::Interrupted => continue,+                Err(e) => return Err(e),+            }++            if read_buf.written().is_empty() {+                break;+            }++            initialized = read_buf.initialized().len() - read_buf.written().len();+            let new_len = buf.len() + read_buf.written().len();+            unsafe {+                buf.set_len(new_len);+            }+        }++        Ok(buf.len() - initial_len)+    }+}++pub fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>+where+    R: Read,+    W: Write,+{+    let mut buf = [MaybeUninit::uninit(); 4096];+    let mut buf = ReadBuf::uninit(&mut buf);+    let mut len = 0;++    loop {+        match reader.read_buf(&mut buf) {+            Ok(()) => {},+            Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,+            Err(e) => return Err(e),+        };++        if buf.written().is_empty() {+            break;+        }++        len += buf.written().len() as u64;+        writer.write_all(buf.written())?;+        buf.clear();+    }++    Ok(len)+}+```++Vectored writes use a similar API:++```rust+/// A possibly-uninitialized version of `IoSliceMut`.+///+/// It is guaranteed to have exactly the same layout and ABI as `IoSliceMut`.+pub struct MaybeUninitIoSliceMut<'a> { ... }++impl<'a> MaybeUninitIoSliceMut<'a> {+    /// Creates a new `MaybeUninitIoSliceMut` from a slice of maybe-uninitialized bytes.+    #[inline]+    pub fn new(buf: &'a mut [MaybeUninit<u8>]) -> MaybeUninitIoSliceMut<'a> { ... }+}++impl<'a> Deref for MaybeUninitIoSliceMut<'a> {+    type Target = [MaybeUninit<u8>];++    ...+}++impl<'a> DerefMut for MaybeUninitIoSliceMut<'a> { ... }+++/// A wrapper over a set of incrementally-initialized buffers.+pub struct ReadBufs<'a> { ... }++impl<'a> ReadBufs<'a> {+    /// Creates a new `ReadBufs` from a set of fully initialized buffers.+    #[inline]+    pub fn new(bufs: &'a mut [IoSliceMut<'a>]) -> ReadBufs<'a> { ... }++    /// Creates a new `ReadBufs` from a set of fully uninitialized buffers.+    ///+    /// Use `assume_initialized` if part of the buffers are known to be already initialized.+    #[inline]+    pub fn uninit(bufs: &'a mut [MaybeUninitIoSliceMut<'a>]) -> ReadBufs<'a> { ... }++    ...+}+```++# Drawbacks+[drawbacks]: #drawbacks++This introduces a nontrivial amount of complexity to one of the standard library's core traits, and results in sets of+almost-but-not-quite identical methods (`read`/`read_buf`, `read_exact`/`read_buf_exact`, etc). It's unfortunate that+an empty implementation of `Read` will compile and stack overflow.++# Rationale and alternatives+[rationale-and-alternatives]: #rationale-and-alternatives++Any solution to this problem needs to satisfy a set of constraints:++1. It needs to be backwards compatible. Duh.+2. It needs to be *efficiently* backwards compatible. Code that doesn't write `unsafe` should not be penalized by the+    new APIs. For example, code working with a reader written before these new APIs were introduced should not become+    slower once that code starts trying to use the new APIs.+3. It must be compatible with `dyn Read`. Trait objects are used pervasively in IO code, so a solution can't depend on+    monomorphization or specialization.+4. It needs to work with both normal and vectored IO (via `read_vectored`).+5. It needs to be composable. Readers are very commonly nested (e.g. `GzipReader<TlsStream<TcpStream>>`), and wrapper+    readers should be able to opt-in to fast paths supported by their inner reader.+6. A reader that does want to work directly with uninitialized memory does, at some reasonable point, need to write the+    word `unsafe`.++This RFC covers the proposed solution. For in-depth coverage of other options and the rationale for this particular+approach over others, please refer to this [Dropbox Paper writeup](https://paper.dropbox.com/doc/IO-Buffer-Initialization--Ax97Yz2_GUH23hVjfDf4JhCAAQ-MvytTgjIOTNpJAS6Mvw38)+or my [discussion with Niko Matsakis](http://smallcultfollowing.com/babysteps/blog/2020/01/20/async-interview-5-steven-fackler/).++The proposal in the Dropbox Paper does differ from the proposal in this RFC in one significant way: its definition of+`read_buf` returns an `io::Result<usize>` like `read` does, and the `ReadBuf` only tracks the initialized region and not+the written-to region:++```rust+pub trait Read {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<usize> { ... }+}+```++This has a subtle but important drawback. From the perspective of code working with a `Read` implementation, the+initialization state of the buffer can be trusted to be correct, but the number of bytes read cannot! This mix of+trusted and untrusted information can be quite a footgun for unsafe code working with a reader. For example,+`read_to_end` needs to remember to assert that the number of bytes read is less than the number of bytes initialized+before calling `set_len` on the `Vec<u8>` that it's reading into. Moving that bit of state into `ReadBuf` avoids the+issue by allowing `ReadBuf` to guarantee that these two values stay consistent.++# Prior art+[prior-art]: #prior-art++The standard library currently has the concept of a buffer "initializer". The `Read` trait has an (unstable) method+which returns an `Initializer` object which can take a `&mut [u8]` of uninitialized memory and initialize it as needed+for use with the associated reader. Then the buffer is just passed to `read` as normal.++The [`tokio::io::AsyncRead`](https://docs.rs/tokio/0.2.21/tokio/io/trait.AsyncRead.html) trait has a somewhat similar+approach, with a `prepare_uninitialized_buffer` method which takes a `&mut [MaybeUninit<u8>]` slice and initializes it+if necessary.

It might be nice to explicitly state that it's believed that this RFC could replace this usage (even if the crate itself decided not to for whatever reason).

sfackler

comment created time in 7 days

Pull request review commentrust-lang/rfcs

RFC: Reading into uninitialized buffers

+- Feature Name: read_buf+- Start Date: 2020/05/18+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Summary+[summary]: #summary++The current design of the `Read` trait is nonoptimal as it requires that the buffer passed to its various methods be+pre-initialized even though the contents will be immediately overwritten. This RFC proposes an interface to allow+implementors and consumers of `Read` types to robustly and soundly work with uninitialized buffers.++# Motivation+[motivation]: #motivation++## Background+[motivation-background]: #motivation-background++The core of the `Read` trait looks like this:++```rust+pub trait Read {+    /// Reads data into `buf`, returning the number of bytes written.+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;+}+```++Code working with a reader needs to create the buffer that will be passed to read; the simple approach is something like+this:++```rust+let mut buf = [0; 1024];+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, that approach isn't ideal since the work spent to zero the buffer is wasted. The reader should be overwriting+the part of the buffer we're working with, after all. Ideally, we wouldn't have to perform any initialization at all:++```rust+let mut buf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() };+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, this is unsound when working with an arbitrary reader. The `Read` trait is not unsafe, so the soundness of+working with an implementation can't depend on the "reasonableness" of the implementation for soundness. The+implementation could read from the buffer, or return the wrong number of bytes read:++```rust+struct BrokenReader;++impl Read for BrokenReader {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        Ok(buf.len())+    }+}++struct BrokenReader2;++impl Read for BrokenReader2 {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        if buf[0] == 0 {+            buf[0] = 1;+        } else {+            buf[0] = 2;+        }++        Ok(1)+    }+}+```++In either case, the `process_data` call above would be working with uninitialized memory. Uninitialized memory is a+dangerous (and often misunderstood) beast. Uninitialized memory does not have an *arbitrary* value; it actually has an+*undefined* value. Undefined values can very quickly turn into undefined behavior. Check out+[Ralf's blog post](https://www.ralfj.de/blog/2019/07/14/uninit.html) for a more extensive discussion of uninitialized+memory.++## But how bad are undefined values really?+[motivation-badness]: #motivation-badness++Are undefined values *really* that bad in practice? Consider a function that tries to use an uninitialized buffer with+a reader:++```rust+fn unsound_read_u32_be<R>(r: &mut R) -> io::Result<u32>+where+    R: Read,+{+    let mut buf: [u8; 4] = unsafe { MaybeUninit::uninit().assume_init() };+    r.read_exact(&mut buf)?;+    Ok(u32::from_be_bytes(buf))+}+```++Now consider this function that tries to use `unsound_read_u32_be`:++```rust+pub fn blammo() -> NonZeroU32 {+    let n = unsound_read_u32_be(&mut BrokenReader).unwrap();+    NonZeroU32::new(n).unwrap_or(NonZeroU32::new(1).unwrap())+}+```++It should clearly only be able to return a nonzero value, but if we compile it using rustc 1.42.0 for the+x86_64-unknown-linux-gnu target, the function [compiles down to this](https://rust.godbolt.org/z/Y9rL-5):++```asm+example::blammo:+        ret+```++That means that it will return whatever arbitrary number happened to be in the `%rax` register. That could very well+happen to be 0, which violates the invariant of `NonZeroU32` and any upstream callers of `blammo` will have a bad time.+Because the value that `unsound_read_u32_be` returned was undefined, the compiler completely removed the check for 0!++We want to be able to take advantage of the improved performance of avoiding buffer initialization without triggering+undefined behavior in safe code.++## Why not just initialize?+[motivation-why]: #motivation-why++If working with uninitialized buffers carries these risks, why should we bother with it at all? Code dealing with IO in+both the standard library and the ecosystem today already works with uninitialized buffers because there are concrete,+nontrivial performance improvements from doing so:++* [The standard library measured](https://github.com/rust-lang/rust/pull/26950) a 7% improvement in benchmarks all the+    way back in 2015.+* [The hyper HTTP library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-554543881) a nontrivial+    improvement in benchmarks.+* [The Quinn QUIC library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553501198) a 0.2%-2.45%+    improvement in benchmarks.++Given that the ecosystem has already found that uninitialized buffer use is important enough to deal with, the standard+library should provide a more robust framework to work with.++In addition, working with regular initialized buffers can be *more complex* than working with uninitialized buffers!+Back in 2015, the standard library's implementation of `Read::read_to_end` was found to be wildly inefficient due to+insufficiently careful management of buffer sizes because it was initializing them.+[The fix](https://github.com/rust-lang/rust/pull/23820) improved the performance of small reads by over 4,000x! If+the buffer did not need to be initialized, the simpler implementation would have been fine.++# Guide-level explanation+[guide-level-explanation]: #guide-level-explanation++The `ReadBuf` type manages a *progressively initialized* buffer of bytes. It is primarily used to avoid buffer+initialization overhead when working with types implementing the `Read` trait. It wraps a buffer of+possibly-uninitialized bytes and tracks how much of the buffer has been initialized and how much of the buffer has been+written to. Tracking the set of initialized bytes allows initialization costs to only be paid once, even if the buffer+is used repeatedly in a loop.++Here's a small example of working with a reader using a `ReadBuf`:++```rust+// The base level buffer uses the `MaybeUninit` type to avoid having to initialize the whole 8kb of memory up-front.+let mut buf = [MaybeUninit::<u8>::uninit(); 8192];++// We then wrap that in a `ReadBuf` to track the state of the buffer.+let mut buf = ReadBuf::uninit(&mut buf);++loop {+    // Read some data into the buffer.+    some_reader.read_buf(&mut buf)?;++    // If nothing was written into the buffer, we're at EOF.+    if buf.written().is_empty() {+        break;+    }++    // Otherwise, process the data.+    process_data(buf.written());++    // And then clear the buffer out so we can read into it again. This just resets the amount of data "written" to 0,+    // but preserves the memory of how much of the buffer has been initialized.+    buf.clear();+}+```++It is important that we created the `ReadBuf` outside of the loop. If we instead created it in each loop iteration we+would fail to preserve the knowledge of how much of it has been initialized.++When implementing `Read`, the author can choose between an entirely safe interface that exposes an initialized buffer,+or an unsafe interface that allows the code to work directly with the uninitialized buffer for higher performance.++A safe `Read` implementation:++```rust+impl Read for MyReader {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        // Get access to the unwritten part of the buffer, making sure it has been fully initialized. Since `ReadBuf`+        // tracks the initialization state of the buffer, this is "free" after the first time it's called.+        let unwritten: &mut [u8] = buf.initialize_unwritten();++        // Fill the whole buffer with some nonsense.+        for (i, byte) in unwritten.iter_mut().enumerate() {+            *byte = i as u8;+        }++        // And indicate that we've written the whole thing.+        let len = unwritten.len();+        buf.add_written(len);++        Ok(())+    }+}+```++An unsafe `Read` implementation:++```rust+impl Read for TcpStream {+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {+        unsafe {+            // Get access to the unwritten part of the buffer, without initializing it. This method is unsafe; we are+            // responsible for ensuing that we don't "de-initialize" portions of it that have previously been+            // initialized.+            let unwritten: &mut [MaybeUninit<u8>] = buf.unwritten_mut();++            // We're just delegating to the libc read function, which returns an `isize`. The return value indicates+            // an error if negative and the number of bytes read otherwise.+            let nread = libc::read(self.fd, unwritten.as_mut_ptr().cast::<libc::c_void>(), unwritten.len());++            if nread < 0 {+                return Err(io::Error::last_os_error());+            }++            let nread = nread as usize;+            // If the read succeeded, tell the buffer that the read-to portion has been initialized. This method is+            // unsafe; we are responsible for ensuring that this portion of the buffer has actually been initialized.+            buf.assume_initialized(nread);+            // And indicate that we've written the bytes as well. Unlike `assume_initialized`, this method is safe,+            // and asserts that the written portion of the buffer does not advance beyond the initialized portion of+            // the buffer. If we didn't call `assume_initialized` above, this call could panic.+            buf.add_written(nread);++            Ok(())+        }+    }+}+```++# Reference-level explanation+[reference-level-explanation]: #reference-level-explanation++```rust+/// A wrapper around a byte buffer that is incrementally written to and initialized.+///+/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the+/// buffer that has been logically written to, a region that has been initialized at some point but not yet logically+/// written to, and a region at the end that is fully uninitialized. The written-to region is guaranteed to be a+/// subset of the initialized region.+pub struct ReadBuf<'a> {+    buf: &'a mut [MaybeUninit<u8>],+    written: usize,+    initialized: usize,+}

At the risk of opening a can of worms, this feels very similar to arrayvec. This makes me wonder a few points:

  1. Could this be made generic over u8, so it could be used for other similar "partial initialization" cases?

  2. Could there be an owning variant of this so that we could do something like

    let mut buf = ReadBufOwned::<const 8192>();
    

    I'm using unstable const generics here, sadly, but I think the ergonomics are nice.

Because of #2, I'd also like to bikeshed about the name <code>Read<b>Buf</b></code> a little. We have Path and PathBuf, where *Buf is the owned version, so I originally wanted to type ReadBufBuf...

sfackler

comment created time in 7 days

Pull request review commentrust-lang/rfcs

RFC: Reading into uninitialized buffers

+- Feature Name: read_buf+- Start Date: 2020/05/18+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Summary+[summary]: #summary++The current design of the `Read` trait is nonoptimal as it requires that the buffer passed to its various methods be+pre-initialized even though the contents will be immediately overwritten. This RFC proposes an interface to allow+implementors and consumers of `Read` types to robustly and soundly work with uninitialized buffers.++# Motivation+[motivation]: #motivation++## Background+[motivation-background]: #motivation-background++The core of the `Read` trait looks like this:++```rust+pub trait Read {+    /// Reads data into `buf`, returning the number of bytes written.+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;+}+```++Code working with a reader needs to create the buffer that will be passed to read; the simple approach is something like+this:++```rust+let mut buf = [0; 1024];+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, that approach isn't ideal since the work spent to zero the buffer is wasted. The reader should be overwriting+the part of the buffer we're working with, after all. Ideally, we wouldn't have to perform any initialization at all:++```rust+let mut buf: [u8; 1024] = unsafe { MaybeUninit::uninit().assume_init() };+let nread = reader.read(&mut buf)?;+process_data(&buf[..nread]);+```++However, this is unsound when working with an arbitrary reader. The `Read` trait is not unsafe, so the soundness of+working with an implementation can't depend on the "reasonableness" of the implementation for soundness. The+implementation could read from the buffer, or return the wrong number of bytes read:++```rust+struct BrokenReader;++impl Read for BrokenReader {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        Ok(buf.len())+    }+}++struct BrokenReader2;++impl Read for BrokenReader2 {+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {+        if buf[0] == 0 {+            buf[0] = 1;+        } else {+            buf[0] = 2;+        }++        Ok(1)+    }+}+```++In either case, the `process_data` call above would be working with uninitialized memory. Uninitialized memory is a+dangerous (and often misunderstood) beast. Uninitialized memory does not have an *arbitrary* value; it actually has an+*undefined* value. Undefined values can very quickly turn into undefined behavior. Check out+[Ralf's blog post](https://www.ralfj.de/blog/2019/07/14/uninit.html) for a more extensive discussion of uninitialized+memory.++## But how bad are undefined values really?+[motivation-badness]: #motivation-badness++Are undefined values *really* that bad in practice? Consider a function that tries to use an uninitialized buffer with+a reader:++```rust+fn unsound_read_u32_be<R>(r: &mut R) -> io::Result<u32>+where+    R: Read,+{+    let mut buf: [u8; 4] = unsafe { MaybeUninit::uninit().assume_init() };+    r.read_exact(&mut buf)?;+    Ok(u32::from_be_bytes(buf))+}+```++Now consider this function that tries to use `unsound_read_u32_be`:++```rust+pub fn blammo() -> NonZeroU32 {+    let n = unsound_read_u32_be(&mut BrokenReader).unwrap();+    NonZeroU32::new(n).unwrap_or(NonZeroU32::new(1).unwrap())+}+```++It should clearly only be able to return a nonzero value, but if we compile it using rustc 1.42.0 for the+x86_64-unknown-linux-gnu target, the function [compiles down to this](https://rust.godbolt.org/z/Y9rL-5):++```asm+example::blammo:+        ret+```++That means that it will return whatever arbitrary number happened to be in the `%rax` register. That could very well+happen to be 0, which violates the invariant of `NonZeroU32` and any upstream callers of `blammo` will have a bad time.+Because the value that `unsound_read_u32_be` returned was undefined, the compiler completely removed the check for 0!++We want to be able to take advantage of the improved performance of avoiding buffer initialization without triggering+undefined behavior in safe code.++## Why not just initialize?+[motivation-why]: #motivation-why++If working with uninitialized buffers carries these risks, why should we bother with it at all? Code dealing with IO in+both the standard library and the ecosystem today already works with uninitialized buffers because there are concrete,+nontrivial performance improvements from doing so:++* [The standard library measured](https://github.com/rust-lang/rust/pull/26950) a 7% improvement in benchmarks all the+    way back in 2015.+* [The hyper HTTP library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-554543881) a nontrivial+    improvement in benchmarks.+* [The Quinn QUIC library measured](https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553501198) a 0.2%-2.45%+    improvement in benchmarks.++Given that the ecosystem has already found that uninitialized buffer use is important enough to deal with, the standard+library should provide a more robust framework to work with.++In addition, working with uninitialized buffers can be *more complex* than working with regular initialized buffers!

This sentence appears to be backwards; isn’t the rest of the paragraph saying that the initialized buffers are more complicated?

sfackler

comment created time in 7 days

delete branch shepmaster/fuzzy-pickles

delete branch : raw-identifiers

delete time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 5cf0b296c7459f0eb7a344f9690112f6ec00240b

Support tokenizing raw identifiers

view details

Jake Goulding

commit sha 043ce98f51c783a8040f867d48777acdf0c3f9f6

Merge pull request #130 from shepmaster/raw-identifiers

view details

push time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 5cf0b296c7459f0eb7a344f9690112f6ec00240b

Support tokenizing raw identifiers

view details

push time in 7 days

PR opened shepmaster/fuzzy-pickles

Support tokenizing raw identifiers enhancement
+48 -12

0 comment

1 changed file

pr created time in 7 days

create barnchshepmaster/fuzzy-pickles

branch : raw-identifiers

created branch time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 1973b13a5a85f4b5a812c97cfe78c95285961672

Remove unneeded semicolon

view details

Jake Goulding

commit sha daced5225b34856ed6cb6ddf112fe3c0f71b25c5

Remove unneeded Into::into calls

view details

Jake Goulding

commit sha 4794f5def800ae906f3c41cb14360953da99320d

Remove unneeded imports

view details

Jake Goulding

commit sha fcc8aa8d12a6d3d9abb2d17f84924100cc8a8ec6

Remove redundant field name

view details

Jake Goulding

commit sha 34d08962bd4be0f7a53e80aabdf32f51d63dd652

Remove redundant return statements

view details

Jake Goulding

commit sha 61410d4d7840550ffe1fb949d2432106e54e16a7

Use mem::take

view details

Jake Goulding

commit sha 5d1d6d64199bef3eb7804c5881e06ebbd3ec3baa

Use single character patterns

view details

Jake Goulding

commit sha c3b6e3a97c51d670cba4a0e8f13f999d11415dd6

Use unwrap_or_else

view details

Jake Goulding

commit sha c974288ea791df9ec158dd00b2c74b9fc4987ca7

Elide some lifetimes

view details

Jake Goulding

commit sha 9e27573cdd64dcbee7fe80a9af77ebd31de82858

Update minimum Rust version

view details

Jake Goulding

commit sha 27a828386f8ed26f49ee3ae1e306207d1019a403

Merge pull request #129 from shepmaster/cleanups

view details

push time in 7 days

delete branch shepmaster/fuzzy-pickles

delete branch : cleanups

delete time in 7 days

push eventshepmaster/fuzzy-pickles

Jake Goulding

commit sha 9e27573cdd64dcbee7fe80a9af77ebd31de82858

Update minimum Rust version

view details

push time in 7 days

pull request commentinteger32llc/rust-playground

Add "Macro expand" tool to Playground

Thank you! I'll try to set aside some time to review this in the near future!

Flowneee

comment created time in 7 days

PR opened shepmaster/fuzzy-pickles

Cleanup for compiler and Clippy warnings maintenance
+25 -35

0 comment

9 changed files

pr created time in 7 days

create barnchshepmaster/fuzzy-pickles

branch : cleanups

created branch time in 7 days

issue commentavr-rust/avrd

Documentation build fail

Plenty of crates Use build scripts to generate source code, so I don’t know why this would be a problem.

Logarithmus

comment created time in 7 days

push eventshepmaster/strata

Jake Goulding

commit sha a0a835f4ebab010c19f272b74a916d325ef0dd2d

Use dyn keyword

view details

Jake Goulding

commit sha 70a68729ea414ff0b678d1b37afa2d63965c2f0c

Enable stricter warnings

view details

Jake Goulding

commit sha 49e79e4ec12254d0f15cba0ed0f011e1ee98571d

Update itertools dependency

view details

push time in 8 days

issue commentrust-lang/rust

impls should not inhibit the unused_item lint

I think that this specifically comes from when Self is used in a trait:

struct Foo;

// Does not prevent the warning
trait Bar0 {}
impl Bar0 for Foo {}

// Does not prevent the warning
trait Bar1 { fn dummy(); }
impl Bar1 for Foo { fn dummy () {} }

// Prevents the warning
trait Bar2 { fn dummy(&self); }
impl Bar2 for Foo { fn dummy (&self) {} }

// Prevents the warning
trait Bar3 { fn dummy() -> Self; }
impl Bar3 for Foo { fn dummy () -> Self { todo!() } }

// Prevents the warning
trait Bar4 { type Dummy; }
impl Bar4 for Foo { type Dummy = Self; }

// Prevents the warning
trait Bar5 { const DUMMY: Self; }
impl Bar5 for Foo { const DUMMY: Self = Self; }
nagisa

comment created time in 8 days

issue commentshepmaster/snafu

Warn when an enum variant is unused

rust-lang/rust#47851 will be very relevant

carols10cents

comment created time in 8 days

delete branch shepmaster/tokio

delete branch : patch-1

delete time in 9 days

issue commentrust-lang/rust

impls should not inhibit the unused_item lint

Does anyone have any suggestions on what part of the code to look at to attempt to fix this?

nagisa

comment created time in 9 days

issue commentshepmaster/snafu

Warn when an enum variant is unused

Hmm, this does seem valuable, but might be tricky and maybe outside of SNAFU's ability.

I think the problem is that we generate structs for each variant, and each struct has methods and/or trait implementations which generate that variant, which means that the variant isn't unused.

I think that the struct itself isn't marked as unused because nothing generated by a procedural macro is marked as unused. I don't know of a way to indicate otherwise.

carols10cents

comment created time in 9 days

issue commentinteger32llc/rust-playground

Share code and compilation output and running output as an image.

This is kind of interesting, but it's not something that I'm personally invested in. Perhaps you could try writing this as a browser extension? Firefox and Chrome both have the ability to create images from the current page, and could interact with the page to get it into the right state.

crlf0710

comment created time in 9 days

pull request commentrust-lang/chalk

Improve coinduction handling in reecursive solver

reecursive

typo

zaharidichev

comment created time in 10 days

issue commentshepmaster/snafu

Error handling for errors that do not implement `std::error::Error`

Your example code is fine, but a recent version of SNAFU did simplify this some. I think this would work:

records.map_err(|_| LcovReadError { string: &lcov_string }.build())?;
ethanabrooks

comment created time in 10 days

Pull request review commentrust-lang/rfcs

RFC: Deduplicate Cargo workspace information

+- Feature Name: `workspace-deduplicate`+- Start Date: 2020-04-13+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Summary+[summary]: #summary++Deduplicate common dependency and metadata directives amongst a set of workspace+crates in Cargo with extensions to the `[workspace]` section in `Cargo.toml`.++# Motivation+[motivation]: #motivation++Cargo has supported workspaces for quite some time now but when managing a large+workspace there is often a good deal of redundancy between member crates in a+workspace. Currently this proposal attempts to tackle a few major areas of+duplication. Many of these areas of duplication are managed either manually or+with scripts, and the goal of this proposal is to largely eliminate the need for+scripts and also the need to manually manage so much.++## Duplication of `[dependencies]` sections++Often when managing a workspace you'll have a lot of crates that all depend on+the same crate. For example many of your crates may depend on `log`. Today you+must write down the same `log` directive in all your manifests:++```toml+[dependencies]+log = "0.3.1"+```++Depending on how many crates you're working on, that's a lot of times to+remember `0.3.1`! Additionally if you'd like to update this dependency, say if a+`1.0.0` release is made, you need to edit every single `Cargo.toml` to make sure+they all stay in sync. This is a lot of duplicated work!++This duplication gets even worse when you start modifying the features of each+crate. For example:++```toml+[dependencies]+log = { version = "0.3.1", features = ['release_max_level_warn'] }+```++If you wanted to consistently write this across many crates it can get quite+cumbersome quite quickly.++## Duplication in inter-dependent crates++When managing a workspace you'll often have a lot workspace members that all+depend on each other. The "blessed" way to do this is actually quite verbose:++```toml+[dependencies]+other-workspace-member = { path = "../other-member", version = "0.2.3" }+```++Here you need to specify *both* `path` and `version`. Using `path` means that+you're depending on exactly that copy on the local filesystem. This also means+that if you depend on any workspace member via a `git` dependency later on it'll+correctly pull in the other workspace members from the git repo. (note that some+projects use `[patch]` to only write down `other-workspace-member = "0.2.3"` but+this causes issues when crates later use git dependencies)++If you never publish to crates.io, `path` is all you need. If crates eventually+get published, though, they also need a `version` directive to know what version+from crates.io you'll be depending on after the publication.++Naturally, with a highly-interconnected workspace which may be relatively large,+this leads to a lot of duplication very quickly. This is a lot of `path` and+`version` directives that you've got to manage.++## Duplication in crate versions++A frequent pattern in Cargo workspaces which publish to crates.io is to have all+the crate at the same semver version. These crates all move in lockstep during+publication and get bumped at the same time.++While a minor papercut this basically means that anyone and everyone who has a+workspace of a lot of crates makes their own homebrew script for updating+versions and managing updates/publications. It'd be quite convenient if we could+standardize across the Rust ecosystem how to manage this information!++## Duplication in crate metadata++The last primary area of duplication that this proposal attempts to tackle is in+crate metadata in the `[package]` section. This includes items such as:++```toml+[package]+authors = []+license = "..."+repository = "..."+documentation = "..."+```++These metadata directives are often duplicated amongst all crates, especially+author/license/repository information. This is a pretty poor experience if you'e+got to keep writing down the information in so many places!++# Guide-level explanation+[guide-level-explanation]: #guide-level-explanation++Cargo's manifest parsing will be updated with new features to support+deduplicating each of the areas above. While all of these new features are+pretty small in their own right, they all add up to greatly reducing the+overhead of managing a workspace of many crates. The list of new features in+Cargo will look like the following:++## Workspace-level Dependencies++The `[workspace]` section can now have a `dependencies` section which works the+same way as the `[dependencies]` section in `Cargo.toml`:++```toml+# in workspace's Cargo.toml+[workspace.dependencies]+log = "0.3.1"+log2 = { version = "2.0.0", package = "log" }+serde = { git = 'https://github.com/serde-rs/serde" }+wasm-bindgen-cli = { path = "crates/cli" }+```++Each workspace member can then reference this section in the workspace with a+new dependency directive:++```toml+# in a workspace member's Cargo.toml+[dependencies]+log = { workspace = true }+```++This directive indicates that the `log` dependency should be looked up from+`workspace.dependencies` in the workspace root. You can reference any name+defined in `[workspace.dependencies]` too:++```toml+[dependencies]+log2 = { workspace = true }+```++## No longer need both `version` and `path` to publish to Crates.io++When you have a `path` dependency, Cargo's current behavior on publication looks+like this:++* If you have a `version` specifier as well, then the `path` key is deleted and+  the crate is uploaded with the specified `version` as a dependency+  requirement.+* If you don't have a `version` specifier, then the dependency directive is+  deleted and crates.io will not learn about this dependency. This is only+  really useful for `dev-dependencies`.++Cargo's behavior will change in this second case, instead following new logic+for a missing `version` specifier. For dev-dependencies where the referenced+package is `publish = false`, then the dependency will be dropped. Otherwise+Cargo will assume that `version = "$dependency_version"` was specified, meaning+that it requires at least the current version and otherwise any+semver-compatible version.

What this proposal means is that clap@3.0.1 depends on clap_derive@3.0.1 which does not exist. This would make us publish clap_derive@3.0.1 which does not have any changes compared with clap_derive@3.0.0.

This is exactly the process that I follow with my crates, so I think it is reasonable.

alexcrichton

comment created time in 12 days

pull request commentrust-lang/rfcs

RFC: `ForbiddenValues` trait to enable more optimizations

Are you saying that I can accomplish my goal today? And if so, how would I go about doing it?

Can I use the “null pointer optimization” for my own non-pointer types?

use std::{mem, num::NonZeroU32};

struct Foo {
    n: NonZeroU32,
}

impl Foo {
    pub fn new(v: u32) -> Self {
        Self { n: Self::convert(v) }
    }
    
    pub fn get(&self) -> u32 {
        u32::from(self.n).wrapping_sub(1)
    }

    pub fn set(&mut self, v: u32) {
        self.n = Self::convert(v)
    }
    
    fn convert(v: u32) -> NonZeroU32 {
        NonZeroU32::new(v.wrapping_add(1)).expect("Out of range")
    }
}

fn main() {
    let mut f = Foo::new(0);
    dbg!(f.get());

    f.set(1);
    dbg!(f.get());

    // f.set(u32::MAX); // panic
    // dbg!(f.get());

    assert_eq!(mem::size_of::<Foo>(), mem::size_of::<Option<Foo>>());
}
tommit

comment created time in 12 days

PR opened tokio-rs/tokio

Typo
+1 -1

0 comment

1 changed file

pr created time in 13 days

push eventshepmaster/tokio

Jake Goulding

commit sha 8ac85ca8462ab17192cd5d22653773c002f45a81

Typo

view details

push time in 13 days

create barnchshepmaster/snafu

branch : more-clash-tests

created branch time in 13 days

issue commentshepmaster/snafu

Macro expansion referencing the core crate is not hygienic

Fixed in 0.6.8

cswinter

comment created time in 14 days

created tagshepmaster/snafu

tag0.6.8

Easily assign underlying errors into domain-specific errors while adding context

created time in 14 days

push eventshepmaster/snafu

Jake Goulding

commit sha f01b0adb477445ec61b00acaceeb3d5353bac428

Update the changelog

view details

Jake Goulding

commit sha fd37d79d4531ed1d3eebffad0d658928eb860cfe

Release 0.6.8

view details

push time in 14 days

delete branch shepmaster/snafu

delete branch : core-clash

delete time in 14 days

push eventshepmaster/snafu

Jake Goulding

commit sha 1c00301a0f6e945cedcb421f80df706b434641a2

Unambiguously reference the core crate Closes #235

view details

Jake Goulding

commit sha 9240502b62a25e0b8915bd328e43a6bba6574ed9

Merge pull request #237 from shepmaster/core-clash

view details

push time in 14 days

PR merged shepmaster/snafu

Unambiguously reference the core crate bug

Closes #235

+19 -15

0 comment

2 changed files

shepmaster

pr closed time in 14 days

issue closedshepmaster/snafu

Macro expansion referencing the core crate is not hygienic

If you declare a module with the name core in a file that uses Snafu derive macro, compilation fails with a bunch of errors like the following:

error[E0433]: failed to resolve: could not find `convert` in `core`
  --> src/lib.rs:40:17
   |
40 | #[derive(Debug, Snafu)]
   |                 ^^^^^ could not find `convert` in `core`
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

closed time in 14 days

cswinter

PR opened shepmaster/snafu

Unambiguously reference the core crate

Closes #235

+19 -15

0 comment

2 changed files

pr created time in 14 days

create barnchshepmaster/snafu

branch : core-clash

created branch time in 14 days

more