6 private links
From slides http://mitsuhiko.pocoo.org/RustAPI.pdf :
APIs are Important
• A library's author's true success metrics are:
• how successful all users are in using the API
• the quality of the output that users achieve by using the API
• the percentage of users making the correct choices
Your User Matters
• When you build a library you should treat it like any other thing
• Define success metrics
• Measure yourself
• Concise: easy to get started
• Good Defaults: easy to get started, trivial to stay on the golden path as it
changes
• Small Surface Area: enable room to breath and innovate, without breaking
users
• Backwards compatible: avoid unnecessary churn to keep users on the golden
path
The Golden Path
• An opinionated path for how to build
• That path might change over time
• Change requires adjustment by users
• Fast change means users being left behind
• Measuring success: users on the golden path (not churning, not staying on
old versions, not hating the upgrade experience, not using old patterns)
Use Defaults to Fight Cargo Cult
• Defaults are hard and of two types:
• Absolute defaults that cannot be changed (i32::default() -> 0)
• Defaults that allow a level of flexibility (Default Hasher: SipHash)
• For defaults to allow flexibility, care has to be taken:
• Set rules and expectations about stability
• Aim for some level of change
Good Defaults
• Default Hasher:
• Hasher is documented to be non portable
• Hasher is documented to change
• No expectation around cross-version/process stability
• A better hasher can be picked, all code ever written benefits at once
More API = More Problems
• The larger the surface, the more of it ends up used
• Less commonly used APIs have the most leaky abstractions
• Inhibits future change: "does someone even use this?"
Hide API Behind Common Abstractions
• Developers are used to these patterns, they are worth exploring:
• Into<T>
• AsRef<T>
• Careful: surface area stays large, but large bound to common and simple
patterns
Into
• Common pairs:
• Into<String>
• Into<Cow<'_, T>>
• Into<YourRuntimeType>
• ToString can be sometimes an interesting alternative to Into<String
Into
• Common pairs:
• Into<String>
• Into<Cow<'_, T>>
• Into<YourRuntimeType>
• ToString can be sometimes an interesting alternative to Into<String
Monomorphization & Compile Times
• Rust loves to inline
• All those different types create
duplicated generated code
• Example: isolate conversions and
call into shared functions to
reduce the total amount of copied
code.
Hide the Onion but create the Onion
• Good APIs are Layered Like Onions
• Only provide the outermost layer first
• Keeps the inner layers flexibility to change
• Over time, consider exposing internal layers under separate stability
guarantees
Explicit Exports
• Hide your internal structure, re-export sensibly
• Your folder structure does not matter to your users
Explicit Fake Modules
• Consider creating modules on the spot for utilities
• For instance "insta" has utility
functions and types that are rarely
useful. The ones I subscribe stability
to are re-exported under a specific
module.
Public but Hidden
• Sometimes stuff needs to be public,
but you don't want anyone to use it.
• Common example: utility functionality
for macros.
• Here both __context and
__context_pair! are public but hidd
Traits are Tricky
• Traits are super useful, but they are tricky
• Fall into two categories:
• Sealed (user should not implement)
• Open (user should implemen
Sealed Traits
• Not really supported, doc hidden
and hackery
• Example in MiniJinja: want to
abstract over types, but I don't
really want to let the user do that
Full Seal
• Uses a private zero sized marker type somewhere
• User cannot implement or invoke as the type is private
Traits are Hard to Discover
• I avoid traits unless I know abstraction over implementations is necessary
• Did you notice that BTreeMap and HashMap are not expressed via traits?
• The usefulness of abstraction even for interchangeable types is sometimes
unclear
• You can always add traits later
Debug
• Put it on all public types
• Consider it on your internal types behind a feature flag
• Super valuable for dbg!() and co
Display
• Makes the type have a representation in format!()
• It also gives it the `.to_string()` method
• Certain types need it in the contract (eg: all errors)
• Recommendation: avoid in most cases unless you implement a custom
integer, string etc
Copy and Clone
• Once granted, impossible to take away
• Neither can be universally provided
• Clone: really useful, consider adding
• If you ever feel you need to take it away, consider Arc<T> internally
• Copy: might inhibit future change, but really useful
• Some types regrettably do not have Copy (eg: Range) and people hate it
Sync and Send
• I cannot give recommendations
• The only one I have: non Send/Sync types are not that bad
• Consider them seriously
Lifetimes and Libraries
• Try to avoid too clever setups
• Consider "Session" abstractions where people only need to temporarily hold
on to data.
Borrowing to Self
• Rust is really bad at this, sometimes you build yourself into a corner
• Best tool I found to date for this is the self_cell crate
• Buffer can be held into itself
Panic vs Error
• Try to avoid panics
• If you do need to panic, consider #[track_caller]
Errors Matter
• Spend some time designing your errors
• Errors deserve attention just as much as your other types
• A talk all by itself, so here the basics:
• Implement std::error::Error on your errors
• Implement source() if you think someone might want to peak into
Live reload rust libraries
FunctionalPlus is a small header-only library supporting you in reducing code noise and in dealing with only one single level of abstraction at a time. By increasing brevity and maintainability of your code it can improve productivity (and fun!) in the long run. It pursues these goals by providing pure and easy-to-use functions that free you from implementing commonly used flows of control over and over again.
A lightweight "signals and slots" implementation using fast delegates.
When GUI programming in C++, delegates and the signals and slots paradigm can vastly simplify your code. It implements the Observer pattern while avoiding all the boilerplate code.
Chlorine is the easiest way to interact with OpenCL compatible devices. It is a header-only C++11 library that allows you to write cross-platform code that runs on GPUs without ever touching the complicated OpenCL API, leaving you free to write code that matters: kernels that process data.
Modern OpenGL wrapper, thin C++14 header-only layer on top of the OpenGL 4.5+ API
Concurrency primitives, safe memory reclamation mechanisms and non-blocking data structures for the research, design and implementation of high performance concurrent systems.
Self-contained C/C++ profiler library for Linux
A curated list of awesome C/C++ frameworks, libraries, resources, and shiny things.
Video++ is a video and image processing library taking advantage of the C++14 standard to ease the writing of fast video and image processing applications. The idea behind Video++ performance is to generate via meta-programming the simplest code possible such that the compiler enable optimizations like loop vectorizing. Its main features are:
Generic N-dimentional image containers.
A growing set of image processing algorithms.
Zero-cost abstractions to easily write image processing algorithms for multicore SIMD CPU processors.
An embeded language to evalute image expressions.
libtins is a high-level, multiplatform C++ network packet sniffing and crafting library.
Its main purpose is to provide the C++ developer an easy, efficient, platform and endianess-independent way to create tools which need to send, receive and manipulate network packets.
The objective of this proposal is to standardize the usage of common data structures within the context of the C language. The existence of a common standard interface for lists, hash tables, flexible arrays, and other containers has several advantages:
User code remains portable across different projects. In C, we all use the FILE abstraction, for instance. This abstraction allows software to be compatible across a large spectrum of machines and operating systems. Imagine what would happen if each project had to develop a file stream abstraction again and again. This is the case when using lists, for instance. Today, we have in all significant projects written in C a list module, and probably other ones like hash tables, etc.
Avoid duplication of effort. Most of the list or hash tables modules can't be debugged completely and are the source of never ending problems.
Lack of standards makes the merging of two projects very difficult since in most cases the interfaces and data structures are slightly different. This leads to a complete rewrite of one of the modules, or to ädapter" software that will translate from one list implementation to the other, adding yet another layer of complexity to the merged project.
The language becomes more expressive since it becomes possible to reason within a high level environment. The lack of operations for handling advanced data structures conditions programmers to use low level solutions like making an array with a fixed maximum size instead of a list even if the programmer would agree that a list would be a more adequate solution to the problem. Confronted to the alternative of developing yet another list module or opting for a low level solution many time constrained programmers will opt for the second solution.
The portable specifications provide a common framework for library writers and compiler/system designers to build compatible yet strongly specialized implementations.
The language becomes easier to analyze mathematically. In their very interesting paper "Precise reasoning for programs using containers", Dillig, Dillig and Aiken 1 enumerate three main points that make program analysis easier using containers:
Understanding the contents of a container doesn't require understanding the container's implementation
Verifying container implementations requires different techniques and degrees of automation than verifying their clients. Hence, separating these two tasks allows us to choose the verification techniques best suited for each purpose.
There are orders of magnitude more clients of a container than there are container implementations. This fact makes it possible to annotate a handful of library interfaces in order to analyze many programs using these containers.
It is possible to abstract from the nature of any container (using the iterator construct) what allows a series of algorithms to be written without having to bind them to a precise data structure. Containers present a uniform interface to the rest of the program.
Dynamically linked shared libraries are an important aspect of GNU/Linux®. They allow executables to dynamically access external functionality at run time and thereby reduce their overall memory footprint (by bringing functionality in when it's needed). This article investigates the process of creating and using dynamic libraries, provides details on the various tools for exploring them, and explores how these libraries work under the hood.
OpenCog is a diverse assemblage of cognitive algorithms, each embodying their own innovations — but what makes the overall architecture powerful is its careful adherence to the principle of cognitive synergy.
FlatBuffers stores serialized data in buffers in a cross-platform way, supporting format evolution that is fully forwards and backwards compatible through a schema. These buffers can be stored in files or sent across the network as-is, and accessed in-place without parsing overhead.