Add a bunch of documentation
This commit is contained in:
parent
257496aae2
commit
9710d17e00
@ -1,3 +1,5 @@
|
||||
/// This module contains simply [`Builder`] and it's related utility Values.
|
||||
/// Builder is the actual struct being modified when building the LLIR.
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
@ -45,7 +47,7 @@ pub struct InstructionHolder {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Builder {
|
||||
pub(crate) struct Builder {
|
||||
modules: Rc<RefCell<Vec<ModuleHolder>>>,
|
||||
}
|
||||
|
||||
@ -237,7 +239,7 @@ impl Builder {
|
||||
}
|
||||
|
||||
impl InstructionValue {
|
||||
pub fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||
use InstructionKind::*;
|
||||
unsafe {
|
||||
match &builder.instr_data(self).kind {
|
||||
@ -316,7 +318,7 @@ impl Type {
|
||||
}
|
||||
|
||||
impl TerminatorKind {
|
||||
pub fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
||||
use TerminatorKind::*;
|
||||
match self {
|
||||
Ret(instr_val) => instr_val.get_type(builder),
|
||||
|
@ -1,3 +1,6 @@
|
||||
/// This module contains all of the relevant code that is needed to compile the
|
||||
/// LLIR ([`Context`]) into LLVM IR. This module is the only one that interfaces
|
||||
/// with the LLVM API.
|
||||
use std::{collections::HashMap, ffi::CString, ptr::null_mut};
|
||||
|
||||
use llvm_sys::{
|
||||
|
@ -1,3 +1,4 @@
|
||||
/// Debug implementations for relevant types
|
||||
use std::fmt::{Debug, Write};
|
||||
|
||||
use crate::{CmpPredicate, InstructionData, InstructionKind, TerminatorKind, builder::*};
|
||||
|
@ -1,3 +1,6 @@
|
||||
/// Reid LLVM Lib is an ergonomic Rust'y API which is used to produce a
|
||||
/// Low-Level IR (LLIR) using [`Context`] and [`Builder`]. This Builder can then
|
||||
/// be used at the end to compile said LLIR into LLVM IR.
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use builder::{BlockValue, Builder, FunctionValue, InstructionValue, ModuleValue};
|
||||
|
@ -29,6 +29,8 @@ fn cstring_to_err(value: *mut c_char) -> Result<(), String> {
|
||||
.map_or(Ok(()), |s| Err(s))
|
||||
}
|
||||
|
||||
/// Utility struct for LLVM's Error Messages, which need to be disposed
|
||||
/// manually.
|
||||
pub struct ErrorMessageHolder(*mut c_char);
|
||||
|
||||
impl ErrorMessageHolder {
|
||||
@ -55,6 +57,8 @@ impl Drop for ErrorMessageHolder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure types for given instructions match. Return Ok(type) if they do,
|
||||
/// and error otherwise.
|
||||
pub fn match_types(
|
||||
lhs: &InstructionValue,
|
||||
rhs: &InstructionValue,
|
||||
|
@ -7,12 +7,16 @@ use reid_lib::{
|
||||
|
||||
use crate::mir::{self, types::ReturnType, TypeKind, VariableReference};
|
||||
|
||||
/// Context that contains all of the given modules as complete codegenerated
|
||||
/// LLIR that can then be finally compiled into LLVM IR.
|
||||
#[derive(Debug)]
|
||||
pub struct CodegenContext<'ctx> {
|
||||
pub modules: Vec<ModuleCodegen<'ctx>>,
|
||||
modules: Vec<ModuleCodegen<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> CodegenContext<'ctx> {
|
||||
/// Compile contained LLIR into LLVM IR and produce `hello.o` and
|
||||
/// `hello.asm`
|
||||
pub fn compile(&self) {
|
||||
for module in &self.modules {
|
||||
module.context.compile();
|
||||
@ -21,6 +25,7 @@ impl<'ctx> CodegenContext<'ctx> {
|
||||
}
|
||||
|
||||
impl mir::Context {
|
||||
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
|
||||
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> {
|
||||
let mut modules = Vec::new();
|
||||
for module in &self.modules {
|
||||
@ -30,9 +35,9 @@ impl mir::Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModuleCodegen<'ctx> {
|
||||
struct ModuleCodegen<'ctx> {
|
||||
pub context: &'ctx Context,
|
||||
pub module: Module<'ctx>,
|
||||
_module: Module<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> std::fmt::Debug for ModuleCodegen<'ctx> {
|
||||
@ -93,7 +98,10 @@ impl mir::Module {
|
||||
}
|
||||
}
|
||||
|
||||
ModuleCodegen { context, module }
|
||||
ModuleCodegen {
|
||||
context,
|
||||
_module: module,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +115,7 @@ pub struct Scope<'ctx, 'a> {
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> Scope<'ctx, 'a> {
|
||||
pub fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
|
||||
fn with_block(&self, block: Block<'ctx>) -> Scope<'ctx, 'a> {
|
||||
Scope {
|
||||
block,
|
||||
function: self.function,
|
||||
@ -120,7 +128,7 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
|
||||
|
||||
/// Takes the block out from this scope, swaps the given block in it's place
|
||||
/// and returns the old block.
|
||||
pub fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
|
||||
fn swap_block(&mut self, block: Block<'ctx>) -> Block<'ctx> {
|
||||
let mut old_block = block;
|
||||
mem::swap(&mut self.block, &mut old_block);
|
||||
old_block
|
||||
@ -128,7 +136,7 @@ impl<'ctx, 'a> Scope<'ctx, 'a> {
|
||||
}
|
||||
|
||||
impl mir::Statement {
|
||||
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
match &self.0 {
|
||||
mir::StmtKind::Let(VariableReference(_, name, _), expression) => {
|
||||
let value = expression.codegen(scope).unwrap();
|
||||
@ -143,7 +151,7 @@ impl mir::Statement {
|
||||
}
|
||||
|
||||
impl mir::IfExpression {
|
||||
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
let condition = self.0.codegen(scope).unwrap();
|
||||
|
||||
// Create blocks
|
||||
@ -208,7 +216,7 @@ impl mir::IfExpression {
|
||||
}
|
||||
|
||||
impl mir::Expression {
|
||||
pub fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
match &self.0 {
|
||||
mir::ExprKind::Variable(varref) => {
|
||||
varref.0.is_known().expect("variable type unknown");
|
||||
@ -304,7 +312,7 @@ impl mir::CmpOperator {
|
||||
}
|
||||
|
||||
impl mir::Block {
|
||||
pub fn codegen<'ctx, 'a>(&self, mut scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
fn codegen<'ctx, 'a>(&self, mut scope: &mut Scope<'ctx, 'a>) -> Option<InstructionValue> {
|
||||
for stmt in &self.statements {
|
||||
stmt.codegen(&mut scope);
|
||||
}
|
||||
@ -325,11 +333,11 @@ impl mir::Block {
|
||||
}
|
||||
|
||||
impl mir::Literal {
|
||||
pub fn as_const(&self, block: &mut Block) -> InstructionValue {
|
||||
fn as_const(&self, block: &mut Block) -> InstructionValue {
|
||||
block.build(self.as_const_kind()).unwrap()
|
||||
}
|
||||
|
||||
pub fn as_const_kind(&self) -> InstructionKind {
|
||||
fn as_const_kind(&self) -> InstructionKind {
|
||||
InstructionKind::Constant(match *self {
|
||||
mir::Literal::I8(val) => ConstValue::I8(val),
|
||||
mir::Literal::I16(val) => ConstValue::I16(val),
|
||||
|
@ -81,6 +81,7 @@ impl From<Token> for String {
|
||||
}
|
||||
}
|
||||
|
||||
/// A token with a position
|
||||
#[derive(Clone)]
|
||||
pub struct FullToken {
|
||||
pub token: Token,
|
||||
@ -98,7 +99,7 @@ impl Debug for FullToken {
|
||||
|
||||
pub type Position = (u32, u32);
|
||||
|
||||
pub struct Cursor<'a> {
|
||||
struct Cursor<'a> {
|
||||
pub position: Position,
|
||||
char_stream: Chars<'a>,
|
||||
}
|
||||
@ -128,6 +129,8 @@ impl<'a> Cursor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Take source text and produce a list of [`FullToken`]s from it, ie.
|
||||
/// tokenizing it.
|
||||
pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error> {
|
||||
let to_tokenize = to_tokenize.into();
|
||||
let mut cursor = Cursor {
|
||||
|
@ -11,11 +11,12 @@ mod pad_adapter;
|
||||
mod token_stream;
|
||||
mod util;
|
||||
|
||||
// TODO:
|
||||
// 1. Make it so that TopLevelStatement can only be import or function def
|
||||
// 2. Make BlockLevelStatement, that has everything TopLevelStatement has now
|
||||
// 3. Make it so all codegen is done with a Block-struct, that represents a
|
||||
// single proper block
|
||||
// TODO (Missing Relevant Features):
|
||||
// - Arrays
|
||||
// - Structs (and custom types as such)
|
||||
// - Extern functions
|
||||
// - Strings
|
||||
// - Loops
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum ReidError {
|
||||
@ -25,10 +26,11 @@ pub enum ReidError {
|
||||
ParserError(#[from] token_stream::Error),
|
||||
#[error("Errors during typecheck: {0:?}")]
|
||||
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
|
||||
// #[error(transparent)]
|
||||
// CodegenError(#[from] codegen::Error),
|
||||
}
|
||||
|
||||
/// Takes in a bit of source code, parses and compiles it and produces `hello.o`
|
||||
/// and `hello.asm` from it, which can be linked using `ld` to produce an
|
||||
/// executable file.
|
||||
pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||
let tokens = lexer::tokenize(source)?;
|
||||
|
||||
@ -51,7 +53,7 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||
dbg!(&ast_module);
|
||||
let mut mir_context = mir::Context::from(vec![ast_module]);
|
||||
|
||||
let state = mir_context.pass(&mut TypeCheck {});
|
||||
let state = mir_context.pass(&mut TypeCheck);
|
||||
dbg!(&state);
|
||||
|
||||
println!("{}", &mir_context);
|
||||
@ -67,9 +69,4 @@ pub fn compile(source: &str) -> Result<String, ReidError> {
|
||||
codegen_modules.compile();
|
||||
|
||||
Ok(String::new())
|
||||
|
||||
// Ok(match cogegen_module.module.print_to_string() {
|
||||
// Ok(v) => v,
|
||||
// Err(e) => panic!("Err: {:?}", e),
|
||||
// })
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::fmt::{write, Debug, Display, Write};
|
||||
use std::fmt::{Debug, Display, Write};
|
||||
|
||||
use crate::pad_adapter::PadAdapter;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
/// In this module are defined structs that are used for performing passes on
|
||||
/// Reid. It contains a simplified version of Reid which must already be
|
||||
/// type-checked beforehand.
|
||||
/// Reid. It contains a simplified version of Reid which can be e.g. typechecked.
|
||||
use crate::token_stream::TokenRange;
|
||||
|
||||
mod display;
|
||||
|
@ -1,3 +1,5 @@
|
||||
/// This module contains relevant code for [`Pass`] and shared code between
|
||||
/// passes. Passes can be performed on Reid MIR to e.g. typecheck the code.
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error as STDError;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/// This module contains code relevant to doing a type checking pass on the MIR.
|
||||
/// During typechecking relevant types are also coerced if possible.
|
||||
use std::{convert::Infallible, iter};
|
||||
|
||||
/// This module contains code relevant to doing a type checking pass on the MIR.
|
||||
use crate::{mir::*, util::try_all};
|
||||
use TypeKind::*;
|
||||
use VagueType::*;
|
||||
@ -29,9 +30,13 @@ pub enum ErrorKind {
|
||||
FunctionAlreadyDefined(String),
|
||||
#[error("Variable not defined: {0}")]
|
||||
VariableAlreadyDefined(String),
|
||||
#[error("Function {0} was given {1} parameters, but {2} were expected")]
|
||||
InvalidAmountParameters(String, usize, usize),
|
||||
}
|
||||
|
||||
pub struct TypeCheck {}
|
||||
/// Struct used to implement a type-checking pass that can be performed on the
|
||||
/// MIR.
|
||||
pub struct TypeCheck;
|
||||
|
||||
impl Pass for TypeCheck {
|
||||
type TError = ErrorKind;
|
||||
@ -123,6 +128,7 @@ impl Block {
|
||||
}
|
||||
|
||||
if let Some((return_kind, expr)) = &mut self.return_expression {
|
||||
// Use function return type as hint if return is hard.
|
||||
let ret_hint_t = match return_kind {
|
||||
ReturnKind::Hard => state.scope.return_type_hint,
|
||||
ReturnKind::Soft => hint_t,
|
||||
@ -193,8 +199,20 @@ impl Expression {
|
||||
.ok_or(ErrorKind::FunctionNotDefined(function_call.name.clone()));
|
||||
|
||||
if let Ok(f) = true_function {
|
||||
if function_call.parameters.len() != f.params.len() {
|
||||
state.ok::<_, Infallible>(Err(ErrorKind::Null), self.1);
|
||||
let param_len_given = function_call.parameters.len();
|
||||
let param_len_expected = f.params.len();
|
||||
|
||||
// Check that there are the same number of parameters given
|
||||
// as expected
|
||||
if param_len_given != param_len_expected {
|
||||
state.ok::<_, Infallible>(
|
||||
Err(ErrorKind::InvalidAmountParameters(
|
||||
function_call.name.clone(),
|
||||
param_len_given,
|
||||
param_len_expected,
|
||||
)),
|
||||
self.1,
|
||||
);
|
||||
}
|
||||
|
||||
let true_params_iter = f.params.into_iter().chain(iter::repeat(Vague(Unknown)));
|
||||
@ -202,6 +220,7 @@ impl Expression {
|
||||
for (param, true_param_t) in
|
||||
function_call.parameters.iter_mut().zip(true_params_iter)
|
||||
{
|
||||
// Typecheck every param separately
|
||||
let param_res = param.typecheck(state, Some(true_param_t));
|
||||
let param_t = state.or_else(param_res, Vague(Unknown), param.1);
|
||||
state.ok(param_t.collapse_into(&true_param_t), param.1);
|
||||
@ -223,15 +242,17 @@ impl Expression {
|
||||
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
|
||||
state.ok(cond_t.collapse_into(&Bool), cond.1);
|
||||
|
||||
let lhs_res = lhs.typecheck(state, hint_t);
|
||||
let lhs_type = state.or_else(lhs_res, Vague(Unknown), lhs.meta);
|
||||
let rhs_type = if let Some(rhs) = rhs {
|
||||
let res = rhs.typecheck(state, hint_t);
|
||||
state.or_else(res, Vague(Unknown), rhs.meta)
|
||||
// Typecheck then/else return types and make sure they are the
|
||||
// same, if else exists.
|
||||
let then_res = lhs.typecheck(state, hint_t);
|
||||
let then_ret_t = state.or_else(then_res, Vague(Unknown), lhs.meta);
|
||||
let else_ret_t = if let Some(else_block) = rhs {
|
||||
let res = else_block.typecheck(state, hint_t);
|
||||
state.or_else(res, Vague(Unknown), else_block.meta)
|
||||
} else {
|
||||
Vague(Unknown)
|
||||
};
|
||||
lhs_type.collapse_into(&rhs_type)
|
||||
then_ret_t.collapse_into(&else_ret_t)
|
||||
}
|
||||
ExprKind::Block(block) => block.typecheck(state, hint_t),
|
||||
}
|
||||
@ -239,6 +260,8 @@ impl Expression {
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
/// Try to coerce this literal, ie. convert it to a more specific type in
|
||||
/// regards to the given hint if any.
|
||||
fn try_coerce(self, hint: Option<TypeKind>) -> Result<Self, ErrorKind> {
|
||||
if let Some(hint) = hint {
|
||||
use Literal as L;
|
||||
@ -314,6 +337,8 @@ fn try_collapse(lhs: &TypeKind, rhs: &TypeKind) -> Result<TypeKind, ErrorKind> {
|
||||
}
|
||||
|
||||
pub trait Collapsable: Sized + Clone {
|
||||
/// Try to narrow two types into one singular type. E.g. Vague(Number) and
|
||||
/// I32 could be narrowed to just I32.
|
||||
fn collapse_into(&self, other: &Self) -> Result<Self, ErrorKind>;
|
||||
}
|
||||
|
||||
|
@ -63,12 +63,3 @@ impl ReturnType for FunctionCall {
|
||||
Ok(self.return_type.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// impl ReturnType for FunctionDefinition {
|
||||
// fn return_type(&self) -> Result<TypeKind, ReturnTypeOther> {
|
||||
// match &self.kind {
|
||||
// FunctionDefinitionKind::Local(block, _) => block.return_type(),
|
||||
// FunctionDefinitionKind::Extern(type_kind) => Ok(type_kind.clone()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
@ -1,29 +1,25 @@
|
||||
// Copyright (c) The Rust Project Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any
|
||||
// person obtaining a copy of this software and associated
|
||||
// documentation files (the "Software"), to deal in the
|
||||
// Software without restriction, including without
|
||||
// limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software
|
||||
// is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice
|
||||
// shall be included in all copies or substantial portions
|
||||
// of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
/// Copied from
|
||||
/// https://github.com/rust-lang/rust/blob/6b3ae3f6e45a33c2d95fa0362c9b2593e567fd34/library/core/src/fmt/builders.rs#L102
|
||||
///
|
||||
/// Copyright (c) The Rust Project Contributors
|
||||
///
|
||||
/// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
/// of this software and associated documentation files (the "Software"), to
|
||||
/// deal in the Software without restriction, including without limitation the
|
||||
/// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
/// sell copies of the Software, and to permit persons to whom the Software is
|
||||
/// furnished to do so, subject to the following conditions:
|
||||
///
|
||||
/// The above copyright notice and this permission notice shall be included in
|
||||
/// all copies or substantial portions of the Software.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
/// IN THE SOFTWARE.
|
||||
use std::fmt;
|
||||
|
||||
pub struct PadAdapter<'buf, 'state> {
|
||||
|
@ -1,8 +1,13 @@
|
||||
/// Contains relevant code for parsing tokens received from
|
||||
/// Lexing/Tokenizing-stage.
|
||||
use crate::{
|
||||
ast::parse::Parse,
|
||||
lexer::{FullToken, Position, Token},
|
||||
};
|
||||
|
||||
/// Utility struct that is able to parse [`FullToken`]s while being
|
||||
/// failure-resistance in that it can backtrack easily, and is able to keep
|
||||
/// track of parsed Token-ranges easily.
|
||||
pub struct TokenStream<'a, 'b> {
|
||||
ref_position: Option<&'b mut usize>,
|
||||
tokens: &'a [FullToken],
|
||||
@ -157,6 +162,8 @@ impl Drop for TokenStream<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Index-range that can be used with the original array of [`FullToken`]s to
|
||||
/// retrieve the precise location of a failure.
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct TokenRange {
|
||||
pub start: usize,
|
||||
|
@ -1,3 +1,6 @@
|
||||
/// Take a list of Results, and try to unwrap all of them. Returns a Result
|
||||
/// which either contains every Result unwrapped, or every error that exists
|
||||
/// within the array.
|
||||
pub fn try_all<U, E>(list: Vec<Result<U, E>>) -> Result<Vec<U>, Vec<E>> {
|
||||
let mut successes = Vec::with_capacity(list.len());
|
||||
let mut failures = Vec::with_capacity(list.len());
|
||||
|
Loading…
Reference in New Issue
Block a user